feat: support parsing the nested list when exporting the document to markdown format (#540)
* feat: support exporting nested list * feat: support parsing the nested list * fix: tests
This commit is contained in:
parent
d1027c4d83
commit
9ae85ea162
11 changed files with 149 additions and 90 deletions
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy_editor/src/core/document/document.dart';
|
||||
import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/node_parser.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class DocumentMarkdownEncoder extends Converter<Document, String> {
|
||||
|
|
@ -19,9 +18,23 @@ class DocumentMarkdownEncoder extends Converter<Document, String> {
|
|||
(element) => element.id == node.type,
|
||||
);
|
||||
if (parser != null) {
|
||||
buffer.write(parser.transform(node));
|
||||
buffer.write(parser.transform(node, this));
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
String convertNodes(
|
||||
List<Node> nodes, {
|
||||
bool withIndent = false,
|
||||
}) {
|
||||
final result = convert(Document(root: pageNode(children: nodes)));
|
||||
if (result.isNotEmpty && withIndent) {
|
||||
return result
|
||||
.split('\n')
|
||||
.map((e) => e.isNotEmpty ? '\t$e' : e)
|
||||
.join('\n');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,17 @@ class BulletedListNodeParser extends NodeParser {
|
|||
const BulletedListNodeParser();
|
||||
|
||||
@override
|
||||
String get id => 'bulleted_list';
|
||||
String get id => BulletedListBlockKeys.type;
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
assert(node.type == 'bulleted_list');
|
||||
|
||||
final delta = node.delta;
|
||||
if (delta == null) {
|
||||
throw Exception('Delta is null');
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||
final delta = node.delta ?? Delta()
|
||||
..insert('');
|
||||
final children = encoder?.convertNodes(node.children, withIndent: true);
|
||||
String markdown = '* ${DeltaMarkdownEncoder().convert(delta)}\n';
|
||||
if (children != null && children.isNotEmpty) {
|
||||
markdown += children;
|
||||
}
|
||||
final markdown = DeltaMarkdownEncoder().convert(delta);
|
||||
final result = '* $markdown';
|
||||
final suffix = node.next == null ? '' : '\n';
|
||||
|
||||
return '$result$suffix';
|
||||
return markdown;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class CodeBlockNodeParser extends NodeParser {
|
|||
String get id => 'code';
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||
assert(node.type == 'code');
|
||||
|
||||
final delta = node.delta;
|
||||
|
|
|
|||
|
|
@ -4,16 +4,12 @@ class HeadingNodeParser extends NodeParser {
|
|||
const HeadingNodeParser();
|
||||
|
||||
@override
|
||||
String get id => 'heading';
|
||||
String get id => HeadingBlockKeys.type;
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
assert(node.type == 'heading');
|
||||
|
||||
final delta = node.delta;
|
||||
if (delta == null) {
|
||||
throw Exception('Delta is null');
|
||||
}
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||
final delta = node.delta ?? Delta()
|
||||
..insert('');
|
||||
final markdown = DeltaMarkdownEncoder().convert(delta);
|
||||
final attributes = node.attributes;
|
||||
final level = attributes[HeadingBlockKeys.level] as int? ?? 1;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||
import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/node_parser.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
|
||||
class ImageNodeParser extends NodeParser {
|
||||
const ImageNodeParser();
|
||||
|
||||
@override
|
||||
String get id => 'image';
|
||||
String get id => ImageBlockKeys.type;
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
|
||||
abstract class NodeParser {
|
||||
const NodeParser();
|
||||
|
||||
String get id;
|
||||
String transform(Node node);
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,18 @@ class NumberedListNodeParser extends NodeParser {
|
|||
const NumberedListNodeParser();
|
||||
|
||||
@override
|
||||
String get id => 'numbered_list';
|
||||
String get id => NumberedListBlockKeys.type;
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
assert(node.type == 'numbered_list');
|
||||
|
||||
final delta = node.delta;
|
||||
if (delta == null) {
|
||||
throw Exception('Delta is null');
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||
final delta = node.delta ?? Delta()
|
||||
..insert('');
|
||||
final number = node.attributes[NumberedListBlockKeys.number] ?? '1';
|
||||
final children = encoder?.convertNodes(node.children, withIndent: true);
|
||||
String markdown = '$number. ${DeltaMarkdownEncoder().convert(delta)}\n';
|
||||
if (children != null && children.isNotEmpty) {
|
||||
markdown += children;
|
||||
}
|
||||
final markdown = DeltaMarkdownEncoder().convert(delta);
|
||||
final result = '1. $markdown'; // FIXME: support parse the number
|
||||
final suffix = node.next == null ? '' : '\n';
|
||||
|
||||
return '$result$suffix';
|
||||
return markdown;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,17 @@ class QuoteNodeParser extends NodeParser {
|
|||
const QuoteNodeParser();
|
||||
|
||||
@override
|
||||
String get id => 'quote';
|
||||
String get id => QuoteBlockKeys.type;
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
assert(node.type == 'quote');
|
||||
|
||||
final delta = node.delta;
|
||||
if (delta == null) {
|
||||
throw Exception('Delta is null');
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||
final delta = node.delta ?? Delta()
|
||||
..insert('');
|
||||
final children = encoder?.convertNodes(node.children, withIndent: true);
|
||||
String markdown = '> ${DeltaMarkdownEncoder().convert(delta)}\n';
|
||||
if (children != null && children.isNotEmpty) {
|
||||
markdown += children;
|
||||
}
|
||||
final markdown = DeltaMarkdownEncoder().convert(delta);
|
||||
final result = '> $markdown';
|
||||
final suffix = node.next == null ? '' : '\n';
|
||||
|
||||
return '$result$suffix';
|
||||
return markdown;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ class TableNodeParser extends NodeParser {
|
|||
String get id => 'table';
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||
final int rowsLen = node.attributes['rowsLen'],
|
||||
colsLen = node.attributes['colsLen'];
|
||||
String result = '';
|
||||
|
|
|
|||
|
|
@ -4,17 +4,24 @@ class TextNodeParser extends NodeParser {
|
|||
const TextNodeParser();
|
||||
|
||||
@override
|
||||
String get id => 'paragraph';
|
||||
String get id => ParagraphBlockKeys.type;
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
final delta = node.delta;
|
||||
if (delta == null) {
|
||||
assert(false, 'Delta is null');
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||
final delta = node.delta ?? Delta()
|
||||
..insert('');
|
||||
final children = encoder?.convertNodes(node.children, withIndent: true);
|
||||
String markdown = DeltaMarkdownEncoder().convert(delta);
|
||||
if (markdown.isEmpty && children == null) {
|
||||
return '';
|
||||
} else if (node
|
||||
.findParent((element) => element.type == TableBlockKeys.type) ==
|
||||
null) {
|
||||
markdown += '\n';
|
||||
}
|
||||
final markdown = DeltaMarkdownEncoder().convert(delta);
|
||||
final suffix = node.next == null ? '' : '\n';
|
||||
return '$markdown$suffix';
|
||||
if (children != null && children.isNotEmpty) {
|
||||
markdown += children;
|
||||
}
|
||||
return markdown;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,22 +4,19 @@ class TodoListNodeParser extends NodeParser {
|
|||
const TodoListNodeParser();
|
||||
|
||||
@override
|
||||
String get id => 'todo_list';
|
||||
String get id => TodoListBlockKeys.type;
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
assert(node.type == 'todo_list');
|
||||
|
||||
final delta = node.delta;
|
||||
if (delta == null) {
|
||||
throw Exception('Delta is null');
|
||||
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||
final delta = node.delta ?? Delta()
|
||||
..insert('');
|
||||
final checked =
|
||||
node.attributes[TodoListBlockKeys.checked] == true ? '- [x]' : '- [ ]';
|
||||
final children = encoder?.convertNodes(node.children, withIndent: true);
|
||||
String markdown = '$checked ${DeltaMarkdownEncoder().convert(delta)}\n';
|
||||
if (children != null && children.isNotEmpty) {
|
||||
markdown += children;
|
||||
}
|
||||
final markdown = DeltaMarkdownEncoder().convert(delta);
|
||||
final attributes = node.attributes;
|
||||
final checked = attributes[TodoListBlockKeys.checked] == true;
|
||||
final result = checked ? '- [x] $markdown' : '- [ ] $markdown';
|
||||
final suffix = node.next == null ? '' : '\n';
|
||||
|
||||
return '$result$suffix';
|
||||
return markdown;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue