Skip to content

Commit c85ab27

Browse files
committed
feat: delta to markdown
1 parent ab664eb commit c85ab27

File tree

7 files changed

+232
-0
lines changed

7 files changed

+232
-0
lines changed

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,47 @@ class _MyHomePageState extends State<MyHomePage> {
111111
if (!darkMode) ...lightEditorStyleExtension,
112112
if (!darkMode) ...lightPlguinStyleExtension,
113113
]);
114+
final delta = Delta();
115+
delta.add(TextInsert('Hello '));
116+
delta.add(
117+
TextInsert(
118+
'World',
119+
attributes: {
120+
BuiltInAttributeKey.bold: true,
121+
BuiltInAttributeKey.italic: true,
122+
},
123+
),
124+
);
125+
delta.add(
126+
TextInsert(
127+
' ',
128+
),
129+
);
130+
delta.add(
131+
TextInsert(
132+
'Again',
133+
attributes: {
134+
BuiltInAttributeKey.italic: true,
135+
},
136+
),
137+
);
138+
delta.add(
139+
TextInsert(
140+
' ',
141+
),
142+
);
143+
delta.add(
144+
TextInsert(
145+
'Again',
146+
attributes: {
147+
BuiltInAttributeKey.href: 'https://google.com',
148+
BuiltInAttributeKey.italic: true,
149+
BuiltInAttributeKey.bold: true,
150+
BuiltInAttributeKey.strikethrough: true,
151+
},
152+
),
153+
);
154+
final result = DeltaMarkdownEncoder().convert(delta);
114155
return Container(
115156
color: darkMode ? Colors.black : Colors.white,
116157
width: MediaQuery.of(context).size.width,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ 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';
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import 'dart:convert';
2+
3+
import 'package:appflowy_editor/appflowy_editor.dart';
4+
5+
/// A [Delta] encoder that encodes a [Delta] to Markdown.
6+
///
7+
/// Only support inline styles, like bold, italic, underline, strike, code.
8+
class DeltaMarkdownEncoder extends Converter<Delta, String> {
9+
@override
10+
String convert(Delta input) {
11+
final buffer = StringBuffer();
12+
final iterator = input.iterator;
13+
while (iterator.moveNext()) {
14+
final op = iterator.current;
15+
if (op is TextInsert) {
16+
final attributes = op.attributes;
17+
if (attributes != null) {
18+
buffer.write(_prefixSyntax(attributes));
19+
buffer.write(op.text);
20+
buffer.write(_suffixSyntax(attributes));
21+
} else {
22+
buffer.write(op.text);
23+
}
24+
}
25+
}
26+
return buffer.toString();
27+
}
28+
29+
String _prefixSyntax(Attributes attributes) {
30+
var syntax = '';
31+
32+
if (attributes[BuiltInAttributeKey.bold] == true &&
33+
attributes[BuiltInAttributeKey.italic] == true) {
34+
syntax += '***';
35+
} else if (attributes[BuiltInAttributeKey.bold] == true) {
36+
syntax += '**';
37+
} else if (attributes[BuiltInAttributeKey.italic] == true) {
38+
syntax += '_';
39+
}
40+
41+
if (attributes[BuiltInAttributeKey.strikethrough] == true) {
42+
syntax += '~~';
43+
}
44+
if (attributes[BuiltInAttributeKey.underline] == true) {
45+
syntax += '<u>';
46+
}
47+
if (attributes[BuiltInAttributeKey.code] == true) {
48+
syntax += '`';
49+
}
50+
51+
if (attributes[BuiltInAttributeKey.href] != null) {
52+
syntax += '[';
53+
}
54+
55+
return syntax;
56+
}
57+
58+
String _suffixSyntax(Attributes attributes) {
59+
var syntax = '';
60+
61+
if (attributes[BuiltInAttributeKey.href] != null) {
62+
syntax += '](${attributes[BuiltInAttributeKey.href]})';
63+
}
64+
65+
if (attributes[BuiltInAttributeKey.code] == true) {
66+
syntax += '`';
67+
}
68+
69+
if (attributes[BuiltInAttributeKey.underline] == true) {
70+
syntax += '</u>';
71+
}
72+
73+
if (attributes[BuiltInAttributeKey.strikethrough] == true) {
74+
syntax += '~~';
75+
}
76+
77+
if (attributes[BuiltInAttributeKey.bold] == true &&
78+
attributes[BuiltInAttributeKey.italic] == true) {
79+
syntax += '***';
80+
} else if (attributes[BuiltInAttributeKey.bold] == true) {
81+
syntax += '**';
82+
} else if (attributes[BuiltInAttributeKey.italic] == true) {
83+
syntax += '_';
84+
}
85+
86+
return syntax;
87+
}
88+
}

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

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

frontend/app_flowy/packages/appflowy_editor/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies:
2727
intl:
2828
flutter_localizations:
2929
sdk: flutter
30+
markdown: ^6.0.1
3031

3132
dev_dependencies:
3233
flutter_test:
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import 'package:appflowy_editor/appflowy_editor.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
4+
void main() async {
5+
group('delta_markdown_encoder.dart', () {
6+
test('bold', () {
7+
final delta = Delta(operations: [
8+
TextInsert('Welcome to '),
9+
TextInsert('AppFlowy', attributes: {
10+
BuiltInAttributeKey.bold: true,
11+
}),
12+
]);
13+
final result = DeltaMarkdownEncoder().convert(delta);
14+
expect(result, 'Welcome to **AppFlowy**');
15+
});
16+
17+
test('italic', () {
18+
final delta = Delta(operations: [
19+
TextInsert('Welcome to '),
20+
TextInsert('AppFlowy', attributes: {
21+
BuiltInAttributeKey.italic: true,
22+
}),
23+
]);
24+
final result = DeltaMarkdownEncoder().convert(delta);
25+
expect(result, 'Welcome to _AppFlowy_');
26+
});
27+
28+
test('underline', () {
29+
final delta = Delta(operations: [
30+
TextInsert('Welcome to '),
31+
TextInsert('AppFlowy', attributes: {
32+
BuiltInAttributeKey.underline: true,
33+
}),
34+
]);
35+
final result = DeltaMarkdownEncoder().convert(delta);
36+
expect(result, 'Welcome to <u>AppFlowy</u>');
37+
});
38+
39+
test('strikethrough', () {
40+
final delta = Delta(operations: [
41+
TextInsert('Welcome to '),
42+
TextInsert('AppFlowy', attributes: {
43+
BuiltInAttributeKey.strikethrough: true,
44+
}),
45+
]);
46+
final result = DeltaMarkdownEncoder().convert(delta);
47+
expect(result, 'Welcome to ~~AppFlowy~~');
48+
});
49+
50+
test('href', () {
51+
final delta = Delta(operations: [
52+
TextInsert('Welcome to '),
53+
TextInsert('AppFlowy', attributes: {
54+
BuiltInAttributeKey.href: 'https://appflowy.io',
55+
}),
56+
]);
57+
final result = DeltaMarkdownEncoder().convert(delta);
58+
expect(result, 'Welcome to [AppFlowy](https://appflowy.io)');
59+
});
60+
61+
test('code', () {
62+
final delta = Delta(operations: [
63+
TextInsert('Welcome to '),
64+
TextInsert('AppFlowy', attributes: {
65+
BuiltInAttributeKey.code: true,
66+
}),
67+
]);
68+
final result = DeltaMarkdownEncoder().convert(delta);
69+
expect(result, 'Welcome to `AppFlowy`');
70+
});
71+
72+
test('composition', () {
73+
final delta = Delta(operations: [
74+
TextInsert('Welcome', attributes: {
75+
BuiltInAttributeKey.code: true,
76+
BuiltInAttributeKey.italic: true,
77+
BuiltInAttributeKey.bold: true,
78+
BuiltInAttributeKey.underline: true,
79+
}),
80+
TextInsert(' '),
81+
TextInsert('to', attributes: {
82+
BuiltInAttributeKey.italic: true,
83+
BuiltInAttributeKey.bold: true,
84+
BuiltInAttributeKey.strikethrough: true,
85+
}),
86+
TextInsert(' '),
87+
TextInsert('AppFlowy', attributes: {
88+
BuiltInAttributeKey.href: 'https://appflowy.io',
89+
BuiltInAttributeKey.bold: true,
90+
BuiltInAttributeKey.italic: true,
91+
}),
92+
]);
93+
final result = DeltaMarkdownEncoder().convert(delta);
94+
expect(
95+
result,
96+
'***<u>`Welcome`</u>*** ***~~to~~*** ***[AppFlowy](https://appflowy.io)***',
97+
);
98+
});
99+
});
100+
}

0 commit comments

Comments
 (0)