import 'package:charcode/charcode.dart'; import 'package:markdown/markdown.dart'; import '../../../flutter_quill.dart' hide Node; /// Parses markdown table and saves the table markdown content into the element attributes. class EmbeddableTableSyntax extends BlockSyntax { /// @nodoc const EmbeddableTableSyntax(); static const _base = TableSyntax(); @override bool canEndBlock(BlockParser parser) => false; @override RegExp get pattern => _base.pattern; @override bool canParse(BlockParser parser) => _base.canParse(parser); /// Parses a table into its three parts: /// /// * a head row of head cells (`` cells) /// * a divider of hyphens and pipes (not rendered) /// * many body rows of body cells (`` cells) @override Node? parse(BlockParser parser) { final columnCount = _columnCount(parser.next!.content); final headCells = _columnCount(parser.current.content); final valBuf = StringBuffer('${parser.current.content}\n${parser.next!.content}'); parser.advance(); if (columnCount != headCells) { return null; } // advance header and divider of hyphens. parser.advance(); while (!parser.isDone && !BlockSyntax.isAtBlockEnd(parser)) { valBuf.write('\n${parser.current.content}'); parser.advance(); } return Element.empty(EmbeddableTable.tableType) ..attributes['data'] = valBuf.toString(); } int _columnCount(String line) { final startIndex = _walkPastOpeningPipe(line); var endIndex = line.length - 1; while (endIndex > 0) { final ch = line.codeUnitAt(endIndex); if (ch == $pipe) { endIndex--; break; } if (ch != $space && ch != $tab) { break; } endIndex--; } return line.substring(startIndex, endIndex + 1).split('|').length; } int _walkPastWhitespace(String line, int index) { while (index < line.length) { final ch = line.codeUnitAt(index); if (ch != $space && ch != $tab) { break; } //ignore: parameter_assignments index++; } return index; } int _walkPastOpeningPipe(String line) { var index = 0; while (index < line.length) { final ch = line.codeUnitAt(index); if (ch == $pipe) { index++; index = _walkPastWhitespace(line, index); } if (ch != $space && ch != $tab) { // No leading pipe. break; } index++; } return index; } } /// An [Embeddable] table that can used to render a table in quill_editor class EmbeddableTable extends BlockEmbed { /// @nodoc EmbeddableTable(String data) : super(tableType, data); /// [Embeddable] type static const tableType = 'x-embed-table'; /// Create from markdown. //ignore: prefer_constructors_over_static_methods static EmbeddableTable fromMdSyntax(Map attributes) => EmbeddableTable(attributes['data']!); /// Outputs table markdown to output. static void toMdSyntax(Embed embed, StringSink out) { out ..writeln(embed.value.data) ..writeln(); } }