items) {
- var anyEmpty = false;
- for (var i = 0; i < items.length; i++) {
- if (items[i].lines.length == 1) {
- continue;
- }
- while (items[i].lines.isNotEmpty &&
- _emptyPattern.hasMatch(items[i].lines.last)) {
- if (i < items.length - 1) {
- anyEmpty = true;
- }
- items[i].lines.removeLast();
- }
- }
- return anyEmpty;
- }
-
- static int _expandedTabLength(String input) {
- var length = 0;
- for (final char in input.codeUnits) {
- length += char == 0x9 ? 4 - (length % 4) : 1;
- }
- return length;
- }
-}
-
-/// Parses unordered lists.
-class UnorderedListSyntax extends ListSyntax {
- const UnorderedListSyntax();
-
- @override
- RegExp get pattern => _ulPattern;
-
- @override
- String get listTag => 'ul';
-}
-
-/// Parses ordered lists.
-class OrderedListSyntax extends ListSyntax {
- const OrderedListSyntax();
-
- @override
- RegExp get pattern => _olPattern;
-
- @override
- String get listTag => 'ol';
-}
-
-/// Parses tables.
-class TableSyntax extends BlockSyntax {
- const TableSyntax();
-
- static final _pipePattern = RegExp(r'\s*\|\s*');
- static final _openingPipe = RegExp(r'^\|\s*');
- static final _closingPipe = RegExp(r'\s*\|$');
-
- @override
- bool get canEndBlock => false;
-
- @override
- bool canParse(BlockParser parser) {
- // Note: matches *next* line, not the current one. We're looking for the
- // bar separating the head row from the body rows.
- return parser.matchesNext(_tablePattern);
- }
-
- /// 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 alignments = parseAlignments(parser.next!);
- final columnCount = alignments.length;
- final headRow = parseRow(parser, alignments, 'th');
- if (headRow.children!.length != columnCount) {
- return null;
- }
- final head = Element('thead', [headRow]);
-
- // Advance past the divider of hyphens.
- parser.advance();
-
- final rows = [];
- while (!parser.isDone && !BlockSyntax.isAtBlockEnd(parser)) {
- final row = parseRow(parser, alignments, 'td');
- while (row.children!.length < columnCount) {
- // Insert synthetic empty cells.
- row.children!.add(Element.empty('td'));
- }
- while (row.children!.length > columnCount) {
- row.children!.removeLast();
- }
- rows.add(row);
- }
- if (rows.isEmpty) {
- return Element('table', [head]);
- } else {
- final body = Element('tbody', rows);
-
- return Element('table', [head, body]);
- }
- }
-
- List parseAlignments(String line) {
- line = line.replaceFirst(_openingPipe, '').replaceFirst(_closingPipe, '');
- return line.split('|').map((column) {
- column = column.trim();
- if (column.startsWith(':') && column.endsWith(':')) {
- return 'center';
- }
- if (column.startsWith(':')) {
- return 'left';
- }
- if (column.endsWith(':')) {
- return 'right';
- }
- return null;
- }).toList();
- }
-
- Element parseRow(
- BlockParser parser, List alignments, String cellType) {
- final line = parser.current
- .replaceFirst(_openingPipe, '')
- .replaceFirst(_closingPipe, '');
- final cells = line.split(_pipePattern);
- parser.advance();
- final row = [];
- String? preCell;
-
- for (var cell in cells) {
- if (preCell != null) {
- cell = preCell + cell;
- preCell = null;
- }
- if (cell.endsWith('\\')) {
- preCell = '${cell.substring(0, cell.length - 1)}|';
- continue;
- }
-
- final contents = UnparsedContent(cell);
- row.add(Element(cellType, [contents]));
- }
-
- for (var i = 0; i < row.length && i < alignments.length; i++) {
- if (alignments[i] == null) {
- continue;
- }
- row[i].attributes['style'] = 'text-align: ${alignments[i]};';
- }
-
- return Element('tr', row);
- }
-}
-
-/// Parses paragraphs of regular text.
-class ParagraphSyntax extends BlockSyntax {
- const ParagraphSyntax();
-
- static final _reflinkDefinitionStart = RegExp(r'[ ]{0,3}\[');
-
- static final _whitespacePattern = RegExp(r'^\s*$');
-
- @override
- bool get canEndBlock => false;
-
- @override
- bool canParse(BlockParser parser) => true;
-
- @override
- Node parse(BlockParser parser) {
- final childLines = [];
-
- // Eat until we hit something that ends a paragraph.
- while (!BlockSyntax.isAtBlockEnd(parser)) {
- childLines.add(parser.current);
- parser.advance();
- }
-
- final paragraphLines = _extractReflinkDefinitions(parser, childLines);
- if (paragraphLines == null) {
- // Paragraph consisted solely of reference link definitions.
- return Text('');
- } else {
- final contents = UnparsedContent(paragraphLines.join('\n'));
- return Element('p', [contents]);
- }
- }
-
- /// Extract reference link definitions from the front of the paragraph, and
- /// return the remaining paragraph lines.
- List? _extractReflinkDefinitions(
- BlockParser parser, List lines) {
- bool lineStartsReflinkDefinition(int i) =>
- lines[i].startsWith(_reflinkDefinitionStart);
-
- var i = 0;
- loopOverDefinitions:
- while (true) {
- // Check for reflink definitions.
- if (!lineStartsReflinkDefinition(i)) {
- // It's paragraph content from here on out.
- break;
- }
- var contents = lines[i];
- var j = i + 1;
- while (j < lines.length) {
- // Check to see if the _next_ line might start a reflink definition.
- // Even if it turns out not to be, but it started with a '[', then it
- // is not a part of _this_ possible reflink definition.
- if (lineStartsReflinkDefinition(j)) {
- // Try to parse [contents] as a reflink definition.
- if (_parseReflinkDefinition(parser, contents)) {
- // Loop again, starting at the next possible reflink definition.
- i = j;
- continue loopOverDefinitions;
- } else {
- // Could not parse [contents] as a reflink definition.
- break;
- }
- } else {
- contents = '$contents\n${lines[j]}';
- j++;
- }
- }
- // End of the block.
- if (_parseReflinkDefinition(parser, contents)) {
- i = j;
- break;
- }
-
- // It may be that there is a reflink definition starting at [i], but it
- // does not extend all the way to [j], such as:
- //
- // [link]: url // line i
- // "title"
- // garbage
- // [link2]: url // line j
- //
- // In this case, [i, i+1] is a reflink definition, and the rest is
- // paragraph content.
- while (j >= i) {
- // This isn't the most efficient loop, what with this big ole'
- // Iterable allocation (`getRange`) followed by a big 'ole String
- // allocation, but we
- // must walk backwards, checking each range.
- contents = lines.getRange(i, j).join('\n');
- if (_parseReflinkDefinition(parser, contents)) {
- // That is the last reflink definition. The rest is paragraph
- // content.
- i = j;
- break;
- }
- j--;
- }
- // The ending was not a reflink definition at all. Just paragraph
- // content.
-
- break;
- }
-
- if (i == lines.length) {
- // No paragraph content.
- return null;
- } else {
- // Ends with paragraph content.
- return lines.sublist(i);
- }
- }
-
- // Parse [contents] as a reference link definition.
- //
- // Also adds the reference link definition to the document.
- //
- // Returns whether [contents] could be parsed as a reference link definition.
- bool _parseReflinkDefinition(BlockParser parser, String contents) {
- final pattern = RegExp(
- // Leading indentation.
- r'''^[ ]{0,3}'''
- // Reference id in brackets, and URL.
- r'''\[((?:\\\]|[^\]])+)\]:\s*(?:<(\S+)>|(\S+))\s*'''
- // Title in double or single quotes, or parens.
- r'''("[^"]+"|'[^']+'|\([^)]+\)|)\s*$''', multiLine: true);
- final match = pattern.firstMatch(contents);
- if (match == null) {
- // Not a reference link definition.
- return false;
- }
- if (match[0]!.length < contents.length) {
- // Trailing text. No good.
- return false;
- }
-
- var label = match[1]!;
- final destination = match[2] ?? match[3];
- var title = match[4];
-
- // The label must contain at least one non-whitespace character.
- if (_whitespacePattern.hasMatch(label)) {
- return false;
- }
-
- if (title == '') {
- // No title.
- title = null;
- } else {
- // Remove "", '', or ().
- title = title!.substring(1, title.length - 1);
- }
-
- // References are case-insensitive, and internal whitespace is compressed.
- label =
- label.toLowerCase().trim().replaceAll(_oneOrMoreWhitespacePattern, ' ');
-
- parser.document.linkReferences
- .putIfAbsent(label, () => LinkReference(label, destination!, title!));
- return true;
- }
-}
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/delta_markdown.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/delta_markdown.dart
deleted file mode 100644
index 2221c185..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/delta_markdown.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-library delta_markdown;
-
-import 'dart:convert';
-
-import 'delta_markdown_decoder.dart';
-import 'delta_markdown_encoder.dart';
-
-/// Codec used to convert between Markdown and Quill deltas.
-const DeltaMarkdownCodec _kCodec = DeltaMarkdownCodec();
-
-String markdownToDelta(String markdown) {
- return _kCodec.decode(markdown);
-}
-
-String deltaToMarkdown(String delta) {
- return _kCodec.encode(delta);
-}
-
-class DeltaMarkdownCodec extends Codec {
- const DeltaMarkdownCodec();
-
- @override
- Converter get decoder => DeltaMarkdownDecoder();
-
- @override
- Converter get encoder => DeltaMarkdownEncoder();
-}
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/delta_markdown_decoder.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/delta_markdown_decoder.dart
deleted file mode 100644
index 2333c93c..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/delta_markdown_decoder.dart
+++ /dev/null
@@ -1,256 +0,0 @@
-import 'dart:collection';
-import 'dart:convert';
-
-import 'package:flutter_quill/flutter_quill.dart'
- show Attribute, AttributeScope, Delta, LinkAttribute;
-
-import 'ast.dart' as ast;
-import 'document.dart';
-
-class DeltaMarkdownDecoder extends Converter {
- @override
- String convert(String input) {
- final lines = input.replaceAll('\r\n', '\n').split('\n');
-
- final markdownDocument = Document().parseLines(lines);
-
- return jsonEncode(_DeltaVisitor().convert(markdownDocument).toJson());
- }
-}
-
-class _DeltaVisitor implements ast.NodeVisitor {
- static final _blockTags =
- RegExp('h1|h2|h3|h4|h5|h6|hr|pre|ul|ol|blockquote|p|pre');
-
- static final _embedTags = RegExp('hr|img');
-
- late Delta delta;
-
- late Queue activeInlineAttributes;
- Attribute? activeBlockAttribute;
- late Set uniqueIds;
-
- ast.Element? previousElement;
- late ast.Element previousToplevelElement;
-
- Delta convert(List nodes) {
- delta = Delta();
- activeInlineAttributes = Queue();
- uniqueIds = {};
-
- for (final node in nodes) {
- node.accept(this);
- }
-
- // Ensure the delta ends with a newline.
- if (delta.length > 0 && delta.last.value != '\n') {
- delta.insert('\n', activeBlockAttribute?.toJson());
- }
-
- return delta;
- }
-
- @override
- void visitText(ast.Text text) {
- // Remove trailing newline
- //final lines = text.text.trim().split('\n');
-
- /*
- final attributes = Map();
- for (final attr in activeInlineAttributes) {
- attributes.addAll(attr.toJson());
- }
-
- for (final l in lines) {
- delta.insert(l, attributes);
- delta.insert('\n', activeBlockAttribute.toJson());
- }*/
-
- final str = text.text;
- //if (str.endsWith('\n')) str = str.substring(0, str.length - 1);
-
- final attributes = {};
- for (final attr in activeInlineAttributes) {
- attributes.addAll(attr.toJson());
- }
-
- var newlineIndex = str.indexOf('\n');
- var startIndex = 0;
- while (newlineIndex != -1) {
- final previousText = str.substring(startIndex, newlineIndex);
- if (previousText.isNotEmpty) {
- delta.insert(previousText, attributes.isNotEmpty ? attributes : null);
- }
- delta.insert('\n', activeBlockAttribute?.toJson());
-
- startIndex = newlineIndex + 1;
- newlineIndex = str.indexOf('\n', newlineIndex + 1);
- }
-
- if (startIndex < str.length) {
- final lastStr = str.substring(startIndex);
- delta.insert(lastStr, attributes.isNotEmpty ? attributes : null);
- }
- }
-
- @override
- bool visitElementBefore(ast.Element element) {
- // Hackish. Separate block-level elements with newlines.
- final attr = _tagToAttribute(element);
-
- if (delta.isNotEmpty && _blockTags.firstMatch(element.tag) != null) {
- if (element.isToplevel) {
- // If the last active block attribute is not a list, we need to finish
- // it off.
- if (previousToplevelElement.tag != 'ul' &&
- previousToplevelElement.tag != 'ol' &&
- previousToplevelElement.tag != 'pre' &&
- previousToplevelElement.tag != 'hr') {
- delta.insert('\n', activeBlockAttribute?.toJson());
- }
-
- // Only separate the blocks if both are paragraphs.
- //
- // TODO(kolja): Determine which behavior we really want here.
- // We can either insert an additional newline or just have the
- // paragraphs as single lines. Zefyr will by default render two lines
- // are different paragraphs so for now we will not add an additonal
- // newline here.
- //
- // if (previousToplevelElement != null &&
- // previousToplevelElement.tag == 'p' &&
- // element.tag == 'p') {
- // delta.insert('\n');
- // }
- } else if (element.tag == 'p' &&
- previousElement != null &&
- !previousElement!.isToplevel &&
- !previousElement!.children!.contains(element)) {
- // Here we have two children of the same toplevel element. These need
- // to be separated by additional newlines.
-
- delta
- // Finish off the last lower-level block.
- ..insert('\n', activeBlockAttribute?.toJson())
- // Add an empty line between the lower-level blocks.
- ..insert('\n', activeBlockAttribute?.toJson());
- }
- }
-
- // Keep track of the top-level block attribute.
- if (element.isToplevel && element.tag != 'hr') {
- // Hacky solution for horizontal rule so that the attribute is not added
- // to the line feed at the end of the line.
- activeBlockAttribute = attr;
- }
-
- if (_embedTags.firstMatch(element.tag) != null) {
- // We write out the element here since the embed has no children or
- // content.
- delta.insert(attr!.toJson());
- } else if (_blockTags.firstMatch(element.tag) == null && attr != null) {
- activeInlineAttributes.addLast(attr);
- }
-
- previousElement = element;
- if (element.isToplevel) {
- previousToplevelElement = element;
- }
-
- if (element.isEmpty) {
- // Empty element like .
- //buffer.write(' />');
-
- if (element.tag == 'br') {
- delta.insert('\n');
- }
-
- return false;
- } else {
- //buffer.write('>');
- return true;
- }
- }
-
- @override
- void visitElementAfter(ast.Element element) {
- if (element.tag == 'li' &&
- (previousToplevelElement.tag == 'ol' ||
- previousToplevelElement.tag == 'ul')) {
- delta.insert('\n', activeBlockAttribute?.toJson());
- }
-
- final attr = _tagToAttribute(element);
- if (attr == null || !attr.isInline || activeInlineAttributes.last != attr) {
- return;
- }
- activeInlineAttributes.removeLast();
-
- // Always keep track of the last element.
- // This becomes relevant if we have something like
- //
- //
- previousElement = element;
- }
-
- /// Uniquifies an id generated from text.
- String uniquifyId(String id) {
- if (!uniqueIds.contains(id)) {
- uniqueIds.add(id);
- return id;
- }
-
- var suffix = 2;
- var suffixedId = '$id-$suffix';
- while (uniqueIds.contains(suffixedId)) {
- suffixedId = '$id-${suffix++}';
- }
- uniqueIds.add(suffixedId);
- return suffixedId;
- }
-
- Attribute? _tagToAttribute(ast.Element el) {
- switch (el.tag) {
- case 'em':
- return Attribute.italic;
- case 'strong':
- return Attribute.bold;
- case 'ul':
- return Attribute.ul;
- case 'ol':
- return Attribute.ol;
- case 'pre':
- return Attribute.codeBlock;
- case 'blockquote':
- return Attribute.blockQuote;
- case 'h1':
- return Attribute.h1;
- case 'h2':
- return Attribute.h2;
- case 'h3':
- return Attribute.h3;
- case 'a':
- final href = el.attributes['href'];
- return LinkAttribute(href);
- case 'img':
- final href = el.attributes['src'];
- return ImageAttribute(href);
- case 'hr':
- return const DividerAttribute();
- }
-
- return null;
- }
-}
-
-class ImageAttribute extends Attribute {
- const ImageAttribute(String? val)
- : super('image', AttributeScope.embeds, val);
-}
-
-class DividerAttribute extends Attribute {
- const DividerAttribute() : super('divider', AttributeScope.embeds, 'hr');
-}
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/delta_markdown_encoder.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/delta_markdown_encoder.dart
deleted file mode 100644
index 2134a92e..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/delta_markdown_encoder.dart
+++ /dev/null
@@ -1,270 +0,0 @@
-import 'dart:convert';
-
-import 'package:collection/collection.dart' show IterableExtension;
-import 'package:flutter_quill/flutter_quill.dart'
- show Attribute, AttributeScope, BlockEmbed, Delta, DeltaIterator, Style;
-
-class DeltaMarkdownEncoder extends Converter {
- static const _lineFeedAsciiCode = 0x0A;
-
- late StringBuffer markdownBuffer;
- late StringBuffer lineBuffer;
-
- Attribute? currentBlockStyle;
- late Style currentInlineStyle;
-
- late List currentBlockLines;
-
- /// Converts the [input] delta to Markdown.
- @override
- String convert(String input) {
- markdownBuffer = StringBuffer();
- lineBuffer = StringBuffer();
- currentInlineStyle = const Style();
- currentBlockLines = [];
-
- final inputJson = jsonDecode(input) as List?;
- if (inputJson is! List) {
- throw ArgumentError('Unexpected formatting of the input delta string.');
- }
- final delta = Delta.fromJson(inputJson);
- final iterator = DeltaIterator(delta);
-
- while (iterator.hasNext) {
- final operation = iterator.next();
-
- if (operation.data is String) {
- final operationData = operation.data as String;
-
- if (!operationData.contains('\n')) {
- _handleInline(lineBuffer, operationData, operation.attributes);
- } else {
- _handleLine(operationData, operation.attributes);
- }
- } else if (operation.data is Map) {
- _handleEmbed(operation.data as Map);
- } else {
- throw ArgumentError('Unexpected formatting of the input delta string.');
- }
- }
-
- _handleBlock(currentBlockStyle); // Close the last block
-
- return markdownBuffer.toString();
- }
-
- void _handleInline(
- StringBuffer buffer,
- String text,
- Map? attributes,
- ) {
- final style = Style.fromJson(attributes);
-
- // First close any current styles if needed
- final markedForRemoval = [];
- // Close the styles in reverse order, e.g. **_ for _**Test**_.
- for (final value
- in currentInlineStyle.attributes.values.toList().reversed) {
- // TODO(tillf): Is block correct?
- if (value.scope == AttributeScope.block) {
- continue;
- }
- if (style.containsKey(value.key)) {
- continue;
- }
-
- final padding = _trimRight(buffer);
- _writeAttribute(buffer, value, close: true);
- if (padding.isNotEmpty) {
- buffer.write(padding);
- }
- markedForRemoval.add(value);
- }
-
- // Make sure to remove all attributes that are marked for removal.
- for (final value in markedForRemoval) {
- currentInlineStyle.attributes.removeWhere((_, v) => v == value);
- }
-
- // Now open any new styles.
- for (final attribute in style.attributes.values) {
- // TODO(tillf): Is block correct?
- if (attribute.scope == AttributeScope.block) {
- continue;
- }
- if (currentInlineStyle.containsKey(attribute.key)) {
- continue;
- }
- final originalText = text;
- text = text.trimLeft();
- final padding = ' ' * (originalText.length - text.length);
- if (padding.isNotEmpty) {
- buffer.write(padding);
- }
- _writeAttribute(buffer, attribute);
- }
-
- // Write the text itself
- buffer.write(text);
- currentInlineStyle = style;
- }
-
- void _handleLine(String data, Map? attributes) {
- final span = StringBuffer();
-
- for (var i = 0; i < data.length; i++) {
- if (data.codeUnitAt(i) == _lineFeedAsciiCode) {
- if (span.isNotEmpty) {
- // Write the span if it's not empty.
- _handleInline(lineBuffer, span.toString(), attributes);
- }
- // Close any open inline styles.
- _handleInline(lineBuffer, '', null);
-
- final lineBlock = Style.fromJson(attributes)
- .attributes
- .values
- .singleWhereOrNull((a) => a.scope == AttributeScope.block);
-
- if (lineBlock == currentBlockStyle) {
- currentBlockLines.add(lineBuffer.toString());
- } else {
- _handleBlock(currentBlockStyle);
- currentBlockLines
- ..clear()
- ..add(lineBuffer.toString());
-
- currentBlockStyle = lineBlock;
- }
- lineBuffer.clear();
-
- span.clear();
- } else {
- span.writeCharCode(data.codeUnitAt(i));
- }
- }
-
- // Remaining span
- if (span.isNotEmpty) {
- _handleInline(lineBuffer, span.toString(), attributes);
- }
- }
-
- void _handleEmbed(Map data) {
- final embed = BlockEmbed(data.keys.first, data.values.first as String);
-
- if (embed.type == 'image') {
- _writeEmbedTag(lineBuffer, embed);
- _writeEmbedTag(lineBuffer, embed, close: true);
- } else if (embed.type == 'divider') {
- _writeEmbedTag(lineBuffer, embed);
- _writeEmbedTag(lineBuffer, embed, close: true);
- }
- }
-
- void _handleBlock(Attribute? blockStyle) {
- if (currentBlockLines.isEmpty) {
- return; // Empty block
- }
-
- // If there was a block before this one, add empty line between the blocks
- if (markdownBuffer.isNotEmpty) {
- markdownBuffer.writeln();
- }
-
- if (blockStyle == null) {
- markdownBuffer
- ..write(currentBlockLines.join('\n'))
- ..writeln();
- } else if (blockStyle == Attribute.codeBlock) {
- _writeAttribute(markdownBuffer, blockStyle);
- markdownBuffer.write(currentBlockLines.join('\n'));
- _writeAttribute(markdownBuffer, blockStyle, close: true);
- markdownBuffer.writeln();
- } else {
- // Dealing with lists or a quote.
- for (final line in currentBlockLines) {
- _writeBlockTag(markdownBuffer, blockStyle);
- markdownBuffer
- ..write(line)
- ..writeln();
- }
- }
- }
-
- String _trimRight(StringBuffer buffer) {
- final text = buffer.toString();
- if (!text.endsWith(' ')) {
- return '';
- }
-
- final result = text.trimRight();
- buffer
- ..clear()
- ..write(result);
- return ' ' * (text.length - result.length);
- }
-
- void _writeAttribute(
- StringBuffer buffer,
- Attribute attribute, {
- bool close = false,
- }) {
- if (attribute.key == Attribute.bold.key) {
- buffer.write('**');
- } else if (attribute.key == Attribute.italic.key) {
- buffer.write('_');
- } else if (attribute.key == Attribute.link.key) {
- buffer.write(!close ? '[' : '](${attribute.value})');
- } else if (attribute == Attribute.codeBlock) {
- buffer.write(!close ? '```\n' : '\n```');
- } else {
- throw ArgumentError('Cannot handle $attribute');
- }
- }
-
- void _writeBlockTag(
- StringBuffer buffer,
- Attribute block, {
- bool close = false,
- }) {
- if (close) {
- return; // no close tag needed for simple blocks.
- }
-
- if (block == Attribute.blockQuote) {
- buffer.write('> ');
- } else if (block == Attribute.ul) {
- buffer.write('* ');
- } else if (block == Attribute.ol) {
- buffer.write('1. ');
- } else if (block.key == Attribute.h1.key && block.value == 1) {
- buffer.write('# ');
- } else if (block.key == Attribute.h2.key && block.value == 2) {
- buffer.write('## ');
- } else if (block.key == Attribute.h3.key && block.value == 3) {
- buffer.write('### ');
- } else {
- throw ArgumentError('Cannot handle block $block');
- }
- }
-
- void _writeEmbedTag(
- StringBuffer buffer,
- BlockEmbed embed, {
- bool close = false,
- }) {
- const kImageType = 'image';
- const kDividerType = 'divider';
-
- if (embed.type == kImageType) {
- if (close) {
- buffer.write('](${embed.data})');
- } else {
- buffer.write('![');
- }
- } else if (embed.type == kDividerType && close) {
- buffer.write('\n---\n\n');
- }
- }
-}
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/document.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/document.dart
deleted file mode 100644
index 890b858c..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/document.dart
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'ast.dart';
-import 'block_parser.dart';
-import 'extension_set.dart';
-import 'inline_parser.dart';
-
-/// Maintains the context needed to parse a Markdown document.
-class Document {
- Document({
- Iterable? blockSyntaxes,
- Iterable? inlineSyntaxes,
- ExtensionSet? extensionSet,
- this.linkResolver,
- this.imageLinkResolver,
- }) : extensionSet = extensionSet ?? ExtensionSet.commonMark {
- _blockSyntaxes
- ..addAll(blockSyntaxes ?? [])
- ..addAll(this.extensionSet.blockSyntaxes);
- _inlineSyntaxes
- ..addAll(inlineSyntaxes ?? [])
- ..addAll(this.extensionSet.inlineSyntaxes);
- }
-
- final Map linkReferences = {};
- final ExtensionSet extensionSet;
- final Resolver? linkResolver;
- final Resolver? imageLinkResolver;
- final _blockSyntaxes = {};
- final _inlineSyntaxes = {};
-
- Iterable get blockSyntaxes => _blockSyntaxes;
- Iterable get inlineSyntaxes => _inlineSyntaxes;
-
- /// Parses the given [lines] of Markdown to a series of AST nodes.
- List parseLines(List lines) {
- final nodes = BlockParser(lines, this).parseLines();
- // Make sure to mark the top level nodes as such.
- for (final n in nodes) {
- n.isToplevel = true;
- }
- _parseInlineContent(nodes);
- return nodes;
- }
-
- /// Parses the given inline Markdown [text] to a series of AST nodes.
- List? parseInline(String text) => InlineParser(text, this).parse();
-
- void _parseInlineContent(List nodes) {
- for (var i = 0; i < nodes.length; i++) {
- final node = nodes[i];
- if (node is UnparsedContent) {
- final inlineNodes = parseInline(node.textContent)!;
- nodes
- ..removeAt(i)
- ..insertAll(i, inlineNodes);
- i += inlineNodes.length - 1;
- } else if (node is Element && node.children != null) {
- _parseInlineContent(node.children!);
- }
- }
- }
-}
-
-/// A [link reference
-/// definition](http://spec.commonmark.org/0.28/#link-reference-definitions).
-class LinkReference {
- /// Construct a [LinkReference], with all necessary fields.
- ///
- /// If the parsed link reference definition does not include a title, use
- /// `null` for the [title] parameter.
- LinkReference(this.label, this.destination, this.title);
-
- /// The [link label](http://spec.commonmark.org/0.28/#link-label).
- ///
- /// Temporarily, this class is also being used to represent the link data for
- /// an inline link (the destination and title), but this should change before
- /// the package is released.
- final String label;
-
- /// The [link destination](http://spec.commonmark.org/0.28/#link-destination).
- final String destination;
-
- /// The [link title](http://spec.commonmark.org/0.28/#link-title).
- final String title;
-}
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/emojis.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/emojis.dart
deleted file mode 100644
index cdb3b694..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/emojis.dart
+++ /dev/null
@@ -1,1510 +0,0 @@
-// GENERATED FILE. DO NOT EDIT.
-//
-// This file was generated from emojilib's emoji data file:
-// https://github.com/muan/emojilib/raw/master/emojis.json
-// at 2018-07-02 15:07:49.422933 by the script, tool/update_emojis.dart.
-
-const emojis = {
- 'grinning': '๐',
- 'grimacing': '๐ฌ',
- 'grin': '๐',
- 'joy': '๐',
- 'rofl': '๐คฃ',
- 'smiley': '๐',
- 'smile': '๐',
- 'sweat_smile': '๐
',
- 'laughing': '๐',
- 'innocent': '๐',
- 'wink': '๐',
- 'blush': '๐',
- 'slightly_smiling_face': '๐',
- 'upside_down_face': '๐',
- 'relaxed': 'โบ๏ธ',
- 'yum': '๐',
- 'relieved': '๐',
- 'heart_eyes': '๐',
- 'kissing_heart': '๐',
- 'kissing': '๐',
- 'kissing_smiling_eyes': '๐',
- 'kissing_closed_eyes': '๐',
- 'stuck_out_tongue_winking_eye': '๐',
- 'zany': '๐คช',
- 'raised_eyebrow': '๐คจ',
- 'monocle': '๐ง',
- 'stuck_out_tongue_closed_eyes': '๐',
- 'stuck_out_tongue': '๐',
- 'money_mouth_face': '๐ค',
- 'nerd_face': '๐ค',
- 'sunglasses': '๐',
- 'star_struck': '๐คฉ',
- 'clown_face': '๐คก',
- 'cowboy_hat_face': '๐ค ',
- 'hugs': '๐ค',
- 'smirk': '๐',
- 'no_mouth': '๐ถ',
- 'neutral_face': '๐',
- 'expressionless': '๐',
- 'unamused': '๐',
- 'roll_eyes': '๐',
- 'thinking': '๐ค',
- 'lying_face': '๐คฅ',
- 'hand_over_mouth': '๐คญ',
- 'shushing': '๐คซ',
- 'symbols_over_mouth': '๐คฌ',
- 'exploding_head': '๐คฏ',
- 'flushed': '๐ณ',
- 'disappointed': '๐',
- 'worried': '๐',
- 'angry': '๐ ',
- 'rage': '๐ก',
- 'pensive': '๐',
- 'confused': '๐',
- 'slightly_frowning_face': '๐',
- 'frowning_face': 'โน',
- 'persevere': '๐ฃ',
- 'confounded': '๐',
- 'tired_face': '๐ซ',
- 'weary': '๐ฉ',
- 'triumph': '๐ค',
- 'open_mouth': '๐ฎ',
- 'scream': '๐ฑ',
- 'fearful': '๐จ',
- 'cold_sweat': '๐ฐ',
- 'hushed': '๐ฏ',
- 'frowning': '๐ฆ',
- 'anguished': '๐ง',
- 'cry': '๐ข',
- 'disappointed_relieved': '๐ฅ',
- 'drooling_face': '๐คค',
- 'sleepy': '๐ช',
- 'sweat': '๐',
- 'sob': '๐ญ',
- 'dizzy_face': '๐ต',
- 'astonished': '๐ฒ',
- 'zipper_mouth_face': '๐ค',
- 'nauseated_face': '๐คข',
- 'sneezing_face': '๐คง',
- 'vomiting': '๐คฎ',
- 'mask': '๐ท',
- 'face_with_thermometer': '๐ค',
- 'face_with_head_bandage': '๐ค',
- 'sleeping': '๐ด',
- 'zzz': '๐ค',
- 'poop': '๐ฉ',
- 'smiling_imp': '๐',
- 'imp': '๐ฟ',
- 'japanese_ogre': '๐น',
- 'japanese_goblin': '๐บ',
- 'skull': '๐',
- 'ghost': '๐ป',
- 'alien': '๐ฝ',
- 'robot': '๐ค',
- 'smiley_cat': '๐บ',
- 'smile_cat': '๐ธ',
- 'joy_cat': '๐น',
- 'heart_eyes_cat': '๐ป',
- 'smirk_cat': '๐ผ',
- 'kissing_cat': '๐ฝ',
- 'scream_cat': '๐',
- 'crying_cat_face': '๐ฟ',
- 'pouting_cat': '๐พ',
- 'palms_up': '๐คฒ',
- 'raised_hands': '๐',
- 'clap': '๐',
- 'wave': '๐',
- 'call_me_hand': '๐ค',
- '+1': '๐',
- '-1': '๐',
- 'facepunch': '๐',
- 'fist': 'โ',
- 'fist_left': '๐ค',
- 'fist_right': '๐ค',
- 'v': 'โ',
- 'ok_hand': '๐',
- 'raised_hand': 'โ',
- 'raised_back_of_hand': '๐ค',
- 'open_hands': '๐',
- 'muscle': '๐ช',
- 'pray': '๐',
- 'handshake': '๐ค',
- 'point_up': 'โ',
- 'point_up_2': '๐',
- 'point_down': '๐',
- 'point_left': '๐',
- 'point_right': '๐',
- 'fu': '๐',
- 'raised_hand_with_fingers_splayed': '๐',
- 'love_you': '๐ค',
- 'metal': '๐ค',
- 'crossed_fingers': '๐ค',
- 'vulcan_salute': '๐',
- 'writing_hand': 'โ',
- 'selfie': '๐คณ',
- 'nail_care': '๐
',
- 'lips': '๐',
- 'tongue': '๐
',
- 'ear': '๐',
- 'nose': '๐',
- 'eye': '๐',
- 'eyes': '๐',
- 'brain': '๐ง ',
- 'bust_in_silhouette': '๐ค',
- 'busts_in_silhouette': '๐ฅ',
- 'speaking_head': '๐ฃ',
- 'baby': '๐ถ',
- 'child': '๐ง',
- 'boy': '๐ฆ',
- 'girl': '๐ง',
- 'adult': '๐ง',
- 'man': '๐จ',
- 'woman': '๐ฉ',
- 'blonde_woman': '๐ฑโโ๏ธ',
- 'blonde_man': '๐ฑ',
- 'bearded_person': '๐ง',
- 'older_adult': '๐ง',
- 'older_man': '๐ด',
- 'older_woman': '๐ต',
- 'man_with_gua_pi_mao': '๐ฒ',
- 'woman_with_headscarf': '๐ง',
- 'woman_with_turban': '๐ณโโ๏ธ',
- 'man_with_turban': '๐ณ',
- 'policewoman': '๐ฎโโ๏ธ',
- 'policeman': '๐ฎ',
- 'construction_worker_woman': '๐ทโโ๏ธ',
- 'construction_worker_man': '๐ท',
- 'guardswoman': '๐โโ๏ธ',
- 'guardsman': '๐',
- 'female_detective': '๐ต๏ธโโ๏ธ',
- 'male_detective': '๐ต',
- 'woman_health_worker': '๐ฉโโ๏ธ',
- 'man_health_worker': '๐จโโ๏ธ',
- 'woman_farmer': '๐ฉโ๐พ',
- 'man_farmer': '๐จโ๐พ',
- 'woman_cook': '๐ฉโ๐ณ',
- 'man_cook': '๐จโ๐ณ',
- 'woman_student': '๐ฉโ๐',
- 'man_student': '๐จโ๐',
- 'woman_singer': '๐ฉโ๐ค',
- 'man_singer': '๐จโ๐ค',
- 'woman_teacher': '๐ฉโ๐ซ',
- 'man_teacher': '๐จโ๐ซ',
- 'woman_factory_worker': '๐ฉโ๐ญ',
- 'man_factory_worker': '๐จโ๐ญ',
- 'woman_technologist': '๐ฉโ๐ป',
- 'man_technologist': '๐จโ๐ป',
- 'woman_office_worker': '๐ฉโ๐ผ',
- 'man_office_worker': '๐จโ๐ผ',
- 'woman_mechanic': '๐ฉโ๐ง',
- 'man_mechanic': '๐จโ๐ง',
- 'woman_scientist': '๐ฉโ๐ฌ',
- 'man_scientist': '๐จโ๐ฌ',
- 'woman_artist': '๐ฉโ๐จ',
- 'man_artist': '๐จโ๐จ',
- 'woman_firefighter': '๐ฉโ๐',
- 'man_firefighter': '๐จโ๐',
- 'woman_pilot': '๐ฉโโ๏ธ',
- 'man_pilot': '๐จโโ๏ธ',
- 'woman_astronaut': '๐ฉโ๐',
- 'man_astronaut': '๐จโ๐',
- 'woman_judge': '๐ฉโโ๏ธ',
- 'man_judge': '๐จโโ๏ธ',
- 'mrs_claus': '๐คถ',
- 'santa': '๐
',
- 'sorceress': '๐งโโ๏ธ',
- 'wizard': '๐งโโ๏ธ',
- 'woman_elf': '๐งโโ๏ธ',
- 'man_elf': '๐งโโ๏ธ',
- 'woman_vampire': '๐งโโ๏ธ',
- 'man_vampire': '๐งโโ๏ธ',
- 'woman_zombie': '๐งโโ๏ธ',
- 'man_zombie': '๐งโโ๏ธ',
- 'woman_genie': '๐งโโ๏ธ',
- 'man_genie': '๐งโโ๏ธ',
- 'mermaid': '๐งโโ๏ธ',
- 'merman': '๐งโโ๏ธ',
- 'woman_fairy': '๐งโโ๏ธ',
- 'man_fairy': '๐งโโ๏ธ',
- 'angel': '๐ผ',
- 'pregnant_woman': '๐คฐ',
- 'breastfeeding': '๐คฑ',
- 'princess': '๐ธ',
- 'prince': '๐คด',
- 'bride_with_veil': '๐ฐ',
- 'man_in_tuxedo': '๐คต',
- 'running_woman': '๐โโ๏ธ',
- 'running_man': '๐',
- 'walking_woman': '๐ถโโ๏ธ',
- 'walking_man': '๐ถ',
- 'dancer': '๐',
- 'man_dancing': '๐บ',
- 'dancing_women': '๐ฏ',
- 'dancing_men': '๐ฏโโ๏ธ',
- 'couple': '๐ซ',
- 'two_men_holding_hands': '๐ฌ',
- 'two_women_holding_hands': '๐ญ',
- 'bowing_woman': '๐โโ๏ธ',
- 'bowing_man': '๐',
- 'man_facepalming': '๐คฆ',
- 'woman_facepalming': '๐คฆโโ๏ธ',
- 'woman_shrugging': '๐คท',
- 'man_shrugging': '๐คทโโ๏ธ',
- 'tipping_hand_woman': '๐',
- 'tipping_hand_man': '๐โโ๏ธ',
- 'no_good_woman': '๐
',
- 'no_good_man': '๐
โโ๏ธ',
- 'ok_woman': '๐',
- 'ok_man': '๐โโ๏ธ',
- 'raising_hand_woman': '๐',
- 'raising_hand_man': '๐โโ๏ธ',
- 'pouting_woman': '๐',
- 'pouting_man': '๐โโ๏ธ',
- 'frowning_woman': '๐',
- 'frowning_man': '๐โโ๏ธ',
- 'haircut_woman': '๐',
- 'haircut_man': '๐โโ๏ธ',
- 'massage_woman': '๐',
- 'massage_man': '๐โโ๏ธ',
- 'woman_in_steamy_room': '๐งโโ๏ธ',
- 'man_in_steamy_room': '๐งโโ๏ธ',
- 'couple_with_heart_woman_man': '๐',
- 'couple_with_heart_woman_woman': '๐ฉโโค๏ธโ๐ฉ',
- 'couple_with_heart_man_man': '๐จโโค๏ธโ๐จ',
- 'couplekiss_man_woman': '๐',
- 'couplekiss_woman_woman': '๐ฉโโค๏ธโ๐โ๐ฉ',
- 'couplekiss_man_man': '๐จโโค๏ธโ๐โ๐จ',
- 'family_man_woman_boy': '๐ช',
- 'family_man_woman_girl': '๐จโ๐ฉโ๐ง',
- 'family_man_woman_girl_boy': '๐จโ๐ฉโ๐งโ๐ฆ',
- 'family_man_woman_boy_boy': '๐จโ๐ฉโ๐ฆโ๐ฆ',
- 'family_man_woman_girl_girl': '๐จโ๐ฉโ๐งโ๐ง',
- 'family_woman_woman_boy': '๐ฉโ๐ฉโ๐ฆ',
- 'family_woman_woman_girl': '๐ฉโ๐ฉโ๐ง',
- 'family_woman_woman_girl_boy': '๐ฉโ๐ฉโ๐งโ๐ฆ',
- 'family_woman_woman_boy_boy': '๐ฉโ๐ฉโ๐ฆโ๐ฆ',
- 'family_woman_woman_girl_girl': '๐ฉโ๐ฉโ๐งโ๐ง',
- 'family_man_man_boy': '๐จโ๐จโ๐ฆ',
- 'family_man_man_girl': '๐จโ๐จโ๐ง',
- 'family_man_man_girl_boy': '๐จโ๐จโ๐งโ๐ฆ',
- 'family_man_man_boy_boy': '๐จโ๐จโ๐ฆโ๐ฆ',
- 'family_man_man_girl_girl': '๐จโ๐จโ๐งโ๐ง',
- 'family_woman_boy': '๐ฉโ๐ฆ',
- 'family_woman_girl': '๐ฉโ๐ง',
- 'family_woman_girl_boy': '๐ฉโ๐งโ๐ฆ',
- 'family_woman_boy_boy': '๐ฉโ๐ฆโ๐ฆ',
- 'family_woman_girl_girl': '๐ฉโ๐งโ๐ง',
- 'family_man_boy': '๐จโ๐ฆ',
- 'family_man_girl': '๐จโ๐ง',
- 'family_man_girl_boy': '๐จโ๐งโ๐ฆ',
- 'family_man_boy_boy': '๐จโ๐ฆโ๐ฆ',
- 'family_man_girl_girl': '๐จโ๐งโ๐ง',
- 'coat': '๐งฅ',
- 'womans_clothes': '๐',
- 'tshirt': '๐',
- 'jeans': '๐',
- 'necktie': '๐',
- 'dress': '๐',
- 'bikini': '๐',
- 'kimono': '๐',
- 'lipstick': '๐',
- 'kiss': '๐',
- 'footprints': '๐ฃ',
- 'high_heel': '๐ ',
- 'sandal': '๐ก',
- 'boot': '๐ข',
- 'mans_shoe': '๐',
- 'athletic_shoe': '๐',
- 'socks': '๐งฆ',
- 'gloves': '๐งค',
- 'scarf': '๐งฃ',
- 'womans_hat': '๐',
- 'tophat': '๐ฉ',
- 'billed_hat': '๐งข',
- 'rescue_worker_helmet': 'โ',
- 'mortar_board': '๐',
- 'crown': '๐',
- 'school_satchel': '๐',
- 'pouch': '๐',
- 'purse': '๐',
- 'handbag': '๐',
- 'briefcase': '๐ผ',
- 'eyeglasses': '๐',
- 'dark_sunglasses': '๐ถ',
- 'ring': '๐',
- 'closed_umbrella': '๐',
- 'dog': '๐ถ',
- 'cat': '๐ฑ',
- 'mouse': '๐ญ',
- 'hamster': '๐น',
- 'rabbit': '๐ฐ',
- 'fox_face': '๐ฆ',
- 'bear': '๐ป',
- 'panda_face': '๐ผ',
- 'koala': '๐จ',
- 'tiger': '๐ฏ',
- 'lion': '๐ฆ',
- 'cow': '๐ฎ',
- 'pig': '๐ท',
- 'pig_nose': '๐ฝ',
- 'frog': '๐ธ',
- 'squid': '๐ฆ',
- 'octopus': '๐',
- 'shrimp': '๐ฆ',
- 'monkey_face': '๐ต',
- 'gorilla': '๐ฆ',
- 'see_no_evil': '๐',
- 'hear_no_evil': '๐',
- 'speak_no_evil': '๐',
- 'monkey': '๐',
- 'chicken': '๐',
- 'penguin': '๐ง',
- 'bird': '๐ฆ',
- 'baby_chick': '๐ค',
- 'hatching_chick': '๐ฃ',
- 'hatched_chick': '๐ฅ',
- 'duck': '๐ฆ',
- 'eagle': '๐ฆ
',
- 'owl': '๐ฆ',
- 'bat': '๐ฆ',
- 'wolf': '๐บ',
- 'boar': '๐',
- 'horse': '๐ด',
- 'unicorn': '๐ฆ',
- 'honeybee': '๐',
- 'bug': '๐',
- 'butterfly': '๐ฆ',
- 'snail': '๐',
- 'beetle': '๐',
- 'ant': '๐',
- 'grasshopper': '๐ฆ',
- 'spider': '๐ท',
- 'scorpion': '๐ฆ',
- 'crab': '๐ฆ',
- 'snake': '๐',
- 'lizard': '๐ฆ',
- 't-rex': '๐ฆ',
- 'sauropod': '๐ฆ',
- 'turtle': '๐ข',
- 'tropical_fish': '๐ ',
- 'fish': '๐',
- 'blowfish': '๐ก',
- 'dolphin': '๐ฌ',
- 'shark': '๐ฆ',
- 'whale': '๐ณ',
- 'whale2': '๐',
- 'crocodile': '๐',
- 'leopard': '๐',
- 'zebra': '๐ฆ',
- 'tiger2': '๐
',
- 'water_buffalo': '๐',
- 'ox': '๐',
- 'cow2': '๐',
- 'deer': '๐ฆ',
- 'dromedary_camel': '๐ช',
- 'camel': '๐ซ',
- 'giraffe': '๐ฆ',
- 'elephant': '๐',
- 'rhinoceros': '๐ฆ',
- 'goat': '๐',
- 'ram': '๐',
- 'sheep': '๐',
- 'racehorse': '๐',
- 'pig2': '๐',
- 'rat': '๐',
- 'mouse2': '๐',
- 'rooster': '๐',
- 'turkey': '๐ฆ',
- 'dove': '๐',
- 'dog2': '๐',
- 'poodle': '๐ฉ',
- 'cat2': '๐',
- 'rabbit2': '๐',
- 'chipmunk': '๐ฟ',
- 'hedgehog': '๐ฆ',
- 'paw_prints': '๐พ',
- 'dragon': '๐',
- 'dragon_face': '๐ฒ',
- 'cactus': '๐ต',
- 'christmas_tree': '๐',
- 'evergreen_tree': '๐ฒ',
- 'deciduous_tree': '๐ณ',
- 'palm_tree': '๐ด',
- 'seedling': '๐ฑ',
- 'herb': '๐ฟ',
- 'shamrock': 'โ',
- 'four_leaf_clover': '๐',
- 'bamboo': '๐',
- 'tanabata_tree': '๐',
- 'leaves': '๐',
- 'fallen_leaf': '๐',
- 'maple_leaf': '๐',
- 'ear_of_rice': '๐พ',
- 'hibiscus': '๐บ',
- 'sunflower': '๐ป',
- 'rose': '๐น',
- 'wilted_flower': '๐ฅ',
- 'tulip': '๐ท',
- 'blossom': '๐ผ',
- 'cherry_blossom': '๐ธ',
- 'bouquet': '๐',
- 'mushroom': '๐',
- 'chestnut': '๐ฐ',
- 'jack_o_lantern': '๐',
- 'shell': '๐',
- 'spider_web': '๐ธ',
- 'earth_americas': '๐',
- 'earth_africa': '๐',
- 'earth_asia': '๐',
- 'full_moon': '๐',
- 'waning_gibbous_moon': '๐',
- 'last_quarter_moon': '๐',
- 'waning_crescent_moon': '๐',
- 'new_moon': '๐',
- 'waxing_crescent_moon': '๐',
- 'first_quarter_moon': '๐',
- 'waxing_gibbous_moon': '๐',
- 'new_moon_with_face': '๐',
- 'full_moon_with_face': '๐',
- 'first_quarter_moon_with_face': '๐',
- 'last_quarter_moon_with_face': '๐',
- 'sun_with_face': '๐',
- 'crescent_moon': '๐',
- 'star': 'โญ',
- 'star2': '๐',
- 'dizzy': '๐ซ',
- 'sparkles': 'โจ',
- 'comet': 'โ',
- 'sunny': 'โ๏ธ',
- 'sun_behind_small_cloud': '๐ค',
- 'partly_sunny': 'โ
',
- 'sun_behind_large_cloud': '๐ฅ',
- 'sun_behind_rain_cloud': '๐ฆ',
- 'cloud': 'โ๏ธ',
- 'cloud_with_rain': '๐ง',
- 'cloud_with_lightning_and_rain': 'โ',
- 'cloud_with_lightning': '๐ฉ',
- 'zap': 'โก',
- 'fire': '๐ฅ',
- 'boom': '๐ฅ',
- 'snowflake': 'โ๏ธ',
- 'cloud_with_snow': '๐จ',
- 'snowman': 'โ',
- 'snowman_with_snow': 'โ',
- 'wind_face': '๐ฌ',
- 'dash': '๐จ',
- 'tornado': '๐ช',
- 'fog': '๐ซ',
- 'open_umbrella': 'โ',
- 'umbrella': 'โ',
- 'droplet': '๐ง',
- 'sweat_drops': '๐ฆ',
- 'ocean': '๐',
- 'green_apple': '๐',
- 'apple': '๐',
- 'pear': '๐',
- 'tangerine': '๐',
- 'lemon': '๐',
- 'banana': '๐',
- 'watermelon': '๐',
- 'grapes': '๐',
- 'strawberry': '๐',
- 'melon': '๐',
- 'cherries': '๐',
- 'peach': '๐',
- 'pineapple': '๐',
- 'coconut': '๐ฅฅ',
- 'kiwi_fruit': '๐ฅ',
- 'avocado': '๐ฅ',
- 'broccoli': '๐ฅฆ',
- 'tomato': '๐
',
- 'eggplant': '๐',
- 'cucumber': '๐ฅ',
- 'carrot': '๐ฅ',
- 'hot_pepper': '๐ถ',
- 'potato': '๐ฅ',
- 'corn': '๐ฝ',
- 'sweet_potato': '๐ ',
- 'peanuts': '๐ฅ',
- 'honey_pot': '๐ฏ',
- 'croissant': '๐ฅ',
- 'bread': '๐',
- 'baguette_bread': '๐ฅ',
- 'pretzel': '๐ฅจ',
- 'cheese': '๐ง',
- 'egg': '๐ฅ',
- 'bacon': '๐ฅ',
- 'steak': '๐ฅฉ',
- 'pancakes': '๐ฅ',
- 'poultry_leg': '๐',
- 'meat_on_bone': '๐',
- 'fried_shrimp': '๐ค',
- 'fried_egg': '๐ณ',
- 'hamburger': '๐',
- 'fries': '๐',
- 'stuffed_flatbread': '๐ฅ',
- 'hotdog': '๐ญ',
- 'pizza': '๐',
- 'sandwich': '๐ฅช',
- 'canned_food': '๐ฅซ',
- 'spaghetti': '๐',
- 'taco': '๐ฎ',
- 'burrito': '๐ฏ',
- 'green_salad': '๐ฅ',
- 'shallow_pan_of_food': '๐ฅ',
- 'ramen': '๐',
- 'stew': '๐ฒ',
- 'fish_cake': '๐ฅ',
- 'fortune_cookie': '๐ฅ ',
- 'sushi': '๐ฃ',
- 'bento': '๐ฑ',
- 'curry': '๐',
- 'rice_ball': '๐',
- 'rice': '๐',
- 'rice_cracker': '๐',
- 'oden': '๐ข',
- 'dango': '๐ก',
- 'shaved_ice': '๐ง',
- 'ice_cream': '๐จ',
- 'icecream': '๐ฆ',
- 'pie': '๐ฅง',
- 'cake': '๐ฐ',
- 'birthday': '๐',
- 'custard': '๐ฎ',
- 'candy': '๐ฌ',
- 'lollipop': '๐ญ',
- 'chocolate_bar': '๐ซ',
- 'popcorn': '๐ฟ',
- 'dumpling': '๐ฅ',
- 'doughnut': '๐ฉ',
- 'cookie': '๐ช',
- 'milk_glass': '๐ฅ',
- 'beer': '๐บ',
- 'beers': '๐ป',
- 'clinking_glasses': '๐ฅ',
- 'wine_glass': '๐ท',
- 'tumbler_glass': '๐ฅ',
- 'cocktail': '๐ธ',
- 'tropical_drink': '๐น',
- 'champagne': '๐พ',
- 'sake': '๐ถ',
- 'tea': '๐ต',
- 'cup_with_straw': '๐ฅค',
- 'coffee': 'โ',
- 'baby_bottle': '๐ผ',
- 'spoon': '๐ฅ',
- 'fork_and_knife': '๐ด',
- 'plate_with_cutlery': '๐ฝ',
- 'bowl_with_spoon': '๐ฅฃ',
- 'takeout_box': '๐ฅก',
- 'chopsticks': '๐ฅข',
- 'soccer': 'โฝ',
- 'basketball': '๐',
- 'football': '๐',
- 'baseball': 'โพ',
- 'tennis': '๐พ',
- 'volleyball': '๐',
- 'rugby_football': '๐',
- '8ball': '๐ฑ',
- 'golf': 'โณ',
- 'golfing_woman': '๐๏ธโโ๏ธ',
- 'golfing_man': '๐',
- 'ping_pong': '๐',
- 'badminton': '๐ธ',
- 'goal_net': '๐ฅ
',
- 'ice_hockey': '๐',
- 'field_hockey': '๐',
- 'cricket': '๐',
- 'ski': '๐ฟ',
- 'skier': 'โท',
- 'snowboarder': '๐',
- 'person_fencing': '๐คบ',
- 'women_wrestling': '๐คผโโ๏ธ',
- 'men_wrestling': '๐คผโโ๏ธ',
- 'woman_cartwheeling': '๐คธโโ๏ธ',
- 'man_cartwheeling': '๐คธโโ๏ธ',
- 'woman_playing_handball': '๐คพโโ๏ธ',
- 'man_playing_handball': '๐คพโโ๏ธ',
- 'ice_skate': 'โธ',
- 'curling_stone': '๐ฅ',
- 'sled': '๐ท',
- 'bow_and_arrow': '๐น',
- 'fishing_pole_and_fish': '๐ฃ',
- 'boxing_glove': '๐ฅ',
- 'martial_arts_uniform': '๐ฅ',
- 'rowing_woman': '๐ฃโโ๏ธ',
- 'rowing_man': '๐ฃ',
- 'climbing_woman': '๐งโโ๏ธ',
- 'climbing_man': '๐งโโ๏ธ',
- 'swimming_woman': '๐โโ๏ธ',
- 'swimming_man': '๐',
- 'woman_playing_water_polo': '๐คฝโโ๏ธ',
- 'man_playing_water_polo': '๐คฝโโ๏ธ',
- 'woman_in_lotus_position': '๐งโโ๏ธ',
- 'man_in_lotus_position': '๐งโโ๏ธ',
- 'surfing_woman': '๐โโ๏ธ',
- 'surfing_man': '๐',
- 'bath': '๐',
- 'basketball_woman': 'โน๏ธโโ๏ธ',
- 'basketball_man': 'โน',
- 'weight_lifting_woman': '๐๏ธโโ๏ธ',
- 'weight_lifting_man': '๐',
- 'biking_woman': '๐ดโโ๏ธ',
- 'biking_man': '๐ด',
- 'mountain_biking_woman': '๐ตโโ๏ธ',
- 'mountain_biking_man': '๐ต',
- 'horse_racing': '๐',
- 'business_suit_levitating': '๐ด',
- 'trophy': '๐',
- 'running_shirt_with_sash': '๐ฝ',
- 'medal_sports': '๐
',
- 'medal_military': '๐',
- '1st_place_medal': '๐ฅ',
- '2nd_place_medal': '๐ฅ',
- '3rd_place_medal': '๐ฅ',
- 'reminder_ribbon': '๐',
- 'rosette': '๐ต',
- 'ticket': '๐ซ',
- 'tickets': '๐',
- 'performing_arts': '๐ญ',
- 'art': '๐จ',
- 'circus_tent': '๐ช',
- 'woman_juggling': '๐คนโโ๏ธ',
- 'man_juggling': '๐คนโโ๏ธ',
- 'microphone': '๐ค',
- 'headphones': '๐ง',
- 'musical_score': '๐ผ',
- 'musical_keyboard': '๐น',
- 'drum': '๐ฅ',
- 'saxophone': '๐ท',
- 'trumpet': '๐บ',
- 'guitar': '๐ธ',
- 'violin': '๐ป',
- 'clapper': '๐ฌ',
- 'video_game': '๐ฎ',
- 'space_invader': '๐พ',
- 'dart': '๐ฏ',
- 'game_die': '๐ฒ',
- 'slot_machine': '๐ฐ',
- 'bowling': '๐ณ',
- 'red_car': '๐',
- 'taxi': '๐',
- 'blue_car': '๐',
- 'bus': '๐',
- 'trolleybus': '๐',
- 'racing_car': '๐',
- 'police_car': '๐',
- 'ambulance': '๐',
- 'fire_engine': '๐',
- 'minibus': '๐',
- 'truck': '๐',
- 'articulated_lorry': '๐',
- 'tractor': '๐',
- 'kick_scooter': '๐ด',
- 'motorcycle': '๐',
- 'bike': '๐ฒ',
- 'motor_scooter': '๐ต',
- 'rotating_light': '๐จ',
- 'oncoming_police_car': '๐',
- 'oncoming_bus': '๐',
- 'oncoming_automobile': '๐',
- 'oncoming_taxi': '๐',
- 'aerial_tramway': '๐ก',
- 'mountain_cableway': '๐ ',
- 'suspension_railway': '๐',
- 'railway_car': '๐',
- 'train': '๐',
- 'monorail': '๐',
- 'bullettrain_side': '๐',
- 'bullettrain_front': '๐
',
- 'light_rail': '๐',
- 'mountain_railway': '๐',
- 'steam_locomotive': '๐',
- 'train2': '๐',
- 'metro': '๐',
- 'tram': '๐',
- 'station': '๐',
- 'flying_saucer': '๐ธ',
- 'helicopter': '๐',
- 'small_airplane': '๐ฉ',
- 'airplane': 'โ๏ธ',
- 'flight_departure': '๐ซ',
- 'flight_arrival': '๐ฌ',
- 'sailboat': 'โต',
- 'motor_boat': '๐ฅ',
- 'speedboat': '๐ค',
- 'ferry': 'โด',
- 'passenger_ship': '๐ณ',
- 'rocket': '๐',
- 'artificial_satellite': '๐ฐ',
- 'seat': '๐บ',
- 'canoe': '๐ถ',
- 'anchor': 'โ',
- 'construction': '๐ง',
- 'fuelpump': 'โฝ',
- 'busstop': '๐',
- 'vertical_traffic_light': '๐ฆ',
- 'traffic_light': '๐ฅ',
- 'checkered_flag': '๐',
- 'ship': '๐ข',
- 'ferris_wheel': '๐ก',
- 'roller_coaster': '๐ข',
- 'carousel_horse': '๐ ',
- 'building_construction': '๐',
- 'foggy': '๐',
- 'tokyo_tower': '๐ผ',
- 'factory': '๐ญ',
- 'fountain': 'โฒ',
- 'rice_scene': '๐',
- 'mountain': 'โฐ',
- 'mountain_snow': '๐',
- 'mount_fuji': '๐ป',
- 'volcano': '๐',
- 'japan': '๐พ',
- 'camping': '๐',
- 'tent': 'โบ',
- 'national_park': '๐',
- 'motorway': '๐ฃ',
- 'railway_track': '๐ค',
- 'sunrise': '๐
',
- 'sunrise_over_mountains': '๐',
- 'desert': '๐',
- 'beach_umbrella': '๐',
- 'desert_island': '๐',
- 'city_sunrise': '๐',
- 'city_sunset': '๐',
- 'cityscape': '๐',
- 'night_with_stars': '๐',
- 'bridge_at_night': '๐',
- 'milky_way': '๐',
- 'stars': '๐ ',
- 'sparkler': '๐',
- 'fireworks': '๐',
- 'rainbow': '๐',
- 'houses': '๐',
- 'european_castle': '๐ฐ',
- 'japanese_castle': '๐ฏ',
- 'stadium': '๐',
- 'statue_of_liberty': '๐ฝ',
- 'house': '๐ ',
- 'house_with_garden': '๐ก',
- 'derelict_house': '๐',
- 'office': '๐ข',
- 'department_store': '๐ฌ',
- 'post_office': '๐ฃ',
- 'european_post_office': '๐ค',
- 'hospital': '๐ฅ',
- 'bank': '๐ฆ',
- 'hotel': '๐จ',
- 'convenience_store': '๐ช',
- 'school': '๐ซ',
- 'love_hotel': '๐ฉ',
- 'wedding': '๐',
- 'classical_building': '๐',
- 'church': 'โช',
- 'mosque': '๐',
- 'synagogue': '๐',
- 'kaaba': '๐',
- 'shinto_shrine': 'โฉ',
- 'watch': 'โ',
- 'iphone': '๐ฑ',
- 'calling': '๐ฒ',
- 'computer': '๐ป',
- 'keyboard': 'โจ',
- 'desktop_computer': '๐ฅ',
- 'printer': '๐จ',
- 'computer_mouse': '๐ฑ',
- 'trackball': '๐ฒ',
- 'joystick': '๐น',
- 'clamp': '๐',
- 'minidisc': '๐ฝ',
- 'floppy_disk': '๐พ',
- 'cd': '๐ฟ',
- 'dvd': '๐',
- 'vhs': '๐ผ',
- 'camera': '๐ท',
- 'camera_flash': '๐ธ',
- 'video_camera': '๐น',
- 'movie_camera': '๐ฅ',
- 'film_projector': '๐ฝ',
- 'film_strip': '๐',
- 'telephone_receiver': '๐',
- 'phone': 'โ๏ธ',
- 'pager': '๐',
- 'fax': '๐ ',
- 'tv': '๐บ',
- 'radio': '๐ป',
- 'studio_microphone': '๐',
- 'level_slider': '๐',
- 'control_knobs': '๐',
- 'stopwatch': 'โฑ',
- 'timer_clock': 'โฒ',
- 'alarm_clock': 'โฐ',
- 'mantelpiece_clock': '๐ฐ',
- 'hourglass_flowing_sand': 'โณ',
- 'hourglass': 'โ',
- 'satellite': '๐ก',
- 'battery': '๐',
- 'electric_plug': '๐',
- 'bulb': '๐ก',
- 'flashlight': '๐ฆ',
- 'candle': '๐ฏ',
- 'wastebasket': '๐',
- 'oil_drum': '๐ข',
- 'money_with_wings': '๐ธ',
- 'dollar': '๐ต',
- 'yen': '๐ด',
- 'euro': '๐ถ',
- 'pound': '๐ท',
- 'moneybag': '๐ฐ',
- 'credit_card': '๐ณ',
- 'gem': '๐',
- 'balance_scale': 'โ',
- 'wrench': '๐ง',
- 'hammer': '๐จ',
- 'hammer_and_pick': 'โ',
- 'hammer_and_wrench': '๐ ',
- 'pick': 'โ',
- 'nut_and_bolt': '๐ฉ',
- 'gear': 'โ',
- 'chains': 'โ',
- 'gun': '๐ซ',
- 'bomb': '๐ฃ',
- 'hocho': '๐ช',
- 'dagger': '๐ก',
- 'crossed_swords': 'โ',
- 'shield': '๐ก',
- 'smoking': '๐ฌ',
- 'skull_and_crossbones': 'โ ',
- 'coffin': 'โฐ',
- 'funeral_urn': 'โฑ',
- 'amphora': '๐บ',
- 'crystal_ball': '๐ฎ',
- 'prayer_beads': '๐ฟ',
- 'barber': '๐',
- 'alembic': 'โ',
- 'telescope': '๐ญ',
- 'microscope': '๐ฌ',
- 'hole': '๐ณ',
- 'pill': '๐',
- 'syringe': '๐',
- 'thermometer': '๐ก',
- 'label': '๐ท',
- 'bookmark': '๐',
- 'toilet': '๐ฝ',
- 'shower': '๐ฟ',
- 'bathtub': '๐',
- 'key': '๐',
- 'old_key': '๐',
- 'couch_and_lamp': '๐',
- 'sleeping_bed': '๐',
- 'bed': '๐',
- 'door': '๐ช',
- 'bellhop_bell': '๐',
- 'framed_picture': '๐ผ',
- 'world_map': '๐บ',
- 'parasol_on_ground': 'โฑ',
- 'moyai': '๐ฟ',
- 'shopping': '๐',
- 'shopping_cart': '๐',
- 'balloon': '๐',
- 'flags': '๐',
- 'ribbon': '๐',
- 'gift': '๐',
- 'confetti_ball': '๐',
- 'tada': '๐',
- 'dolls': '๐',
- 'wind_chime': '๐',
- 'crossed_flags': '๐',
- 'izakaya_lantern': '๐ฎ',
- 'email': 'โ๏ธ',
- 'envelope_with_arrow': '๐ฉ',
- 'incoming_envelope': '๐จ',
- 'e-mail': '๐ง',
- 'love_letter': '๐',
- 'postbox': '๐ฎ',
- 'mailbox_closed': '๐ช',
- 'mailbox': '๐ซ',
- 'mailbox_with_mail': '๐ฌ',
- 'mailbox_with_no_mail': '๐ญ',
- 'package': '๐ฆ',
- 'postal_horn': '๐ฏ',
- 'inbox_tray': '๐ฅ',
- 'outbox_tray': '๐ค',
- 'scroll': '๐',
- 'page_with_curl': '๐',
- 'bookmark_tabs': '๐',
- 'bar_chart': '๐',
- 'chart_with_upwards_trend': '๐',
- 'chart_with_downwards_trend': '๐',
- 'page_facing_up': '๐',
- 'date': '๐
',
- 'calendar': '๐',
- 'spiral_calendar': '๐',
- 'card_index': '๐',
- 'card_file_box': '๐',
- 'ballot_box': '๐ณ',
- 'file_cabinet': '๐',
- 'clipboard': '๐',
- 'spiral_notepad': '๐',
- 'file_folder': '๐',
- 'open_file_folder': '๐',
- 'card_index_dividers': '๐',
- 'newspaper_roll': '๐',
- 'newspaper': '๐ฐ',
- 'notebook': '๐',
- 'closed_book': '๐',
- 'green_book': '๐',
- 'blue_book': '๐',
- 'orange_book': '๐',
- 'notebook_with_decorative_cover': '๐',
- 'ledger': '๐',
- 'books': '๐',
- 'open_book': '๐',
- 'link': '๐',
- 'paperclip': '๐',
- 'paperclips': '๐',
- 'scissors': 'โ๏ธ',
- 'triangular_ruler': '๐',
- 'straight_ruler': '๐',
- 'pushpin': '๐',
- 'round_pushpin': '๐',
- 'triangular_flag_on_post': '๐ฉ',
- 'white_flag': '๐ณ',
- 'black_flag': '๐ด',
- 'rainbow_flag': '๐ณ๏ธโ๐',
- 'closed_lock_with_key': '๐',
- 'lock': '๐',
- 'unlock': '๐',
- 'lock_with_ink_pen': '๐',
- 'pen': '๐',
- 'fountain_pen': '๐',
- 'black_nib': 'โ๏ธ',
- 'memo': '๐',
- 'pencil2': 'โ๏ธ',
- 'crayon': '๐',
- 'paintbrush': '๐',
- 'mag': '๐',
- 'mag_right': '๐',
- 'heart': 'โค๏ธ',
- 'orange_heart': '๐งก',
- 'yellow_heart': '๐',
- 'green_heart': '๐',
- 'blue_heart': '๐',
- 'purple_heart': '๐',
- 'black_heart': '๐ค',
- 'broken_heart': '๐',
- 'heavy_heart_exclamation': 'โฃ',
- 'two_hearts': '๐',
- 'revolving_hearts': '๐',
- 'heartbeat': '๐',
- 'heartpulse': '๐',
- 'sparkling_heart': '๐',
- 'cupid': '๐',
- 'gift_heart': '๐',
- 'heart_decoration': '๐',
- 'peace_symbol': 'โฎ',
- 'latin_cross': 'โ',
- 'star_and_crescent': 'โช',
- 'om': '๐',
- 'wheel_of_dharma': 'โธ',
- 'star_of_david': 'โก',
- 'six_pointed_star': '๐ฏ',
- 'menorah': '๐',
- 'yin_yang': 'โฏ',
- 'orthodox_cross': 'โฆ',
- 'place_of_worship': '๐',
- 'ophiuchus': 'โ',
- 'aries': 'โ',
- 'taurus': 'โ',
- 'gemini': 'โ',
- 'cancer': 'โ',
- 'leo': 'โ',
- 'virgo': 'โ',
- 'libra': 'โ',
- 'scorpius': 'โ',
- 'sagittarius': 'โ',
- 'capricorn': 'โ',
- 'aquarius': 'โ',
- 'pisces': 'โ',
- 'id': '๐',
- 'atom_symbol': 'โ',
- 'u7a7a': '๐ณ',
- 'u5272': '๐น',
- 'radioactive': 'โข',
- 'biohazard': 'โฃ',
- 'mobile_phone_off': '๐ด',
- 'vibration_mode': '๐ณ',
- 'u6709': '๐ถ',
- 'u7121': '๐',
- 'u7533': '๐ธ',
- 'u55b6': '๐บ',
- 'u6708': '๐ท๏ธ',
- 'eight_pointed_black_star': 'โด๏ธ',
- 'vs': '๐',
- 'accept': '๐',
- 'white_flower': '๐ฎ',
- 'ideograph_advantage': '๐',
- 'secret': 'ใ๏ธ',
- 'congratulations': 'ใ๏ธ',
- 'u5408': '๐ด',
- 'u6e80': '๐ต',
- 'u7981': '๐ฒ',
- 'a': '๐
ฐ๏ธ',
- 'b': '๐
ฑ๏ธ',
- 'ab': '๐',
- 'cl': '๐',
- 'o2': '๐
พ๏ธ',
- 'sos': '๐',
- 'no_entry': 'โ',
- 'name_badge': '๐',
- 'no_entry_sign': '๐ซ',
- 'x': 'โ',
- 'o': 'โญ',
- 'stop_sign': '๐',
- 'anger': '๐ข',
- 'hotsprings': 'โจ๏ธ',
- 'no_pedestrians': '๐ท',
- 'do_not_litter': '๐ฏ',
- 'no_bicycles': '๐ณ',
- 'non-potable_water': '๐ฑ',
- 'underage': '๐',
- 'no_mobile_phones': '๐ต',
- 'exclamation': 'โ',
- 'grey_exclamation': 'โ',
- 'question': 'โ',
- 'grey_question': 'โ',
- 'bangbang': 'โผ๏ธ',
- 'interrobang': 'โ๏ธ',
- '100': '๐ฏ',
- 'low_brightness': '๐
',
- 'high_brightness': '๐',
- 'trident': '๐ฑ',
- 'fleur_de_lis': 'โ',
- 'part_alternation_mark': 'ใฝ๏ธ',
- 'warning': 'โ ๏ธ',
- 'children_crossing': '๐ธ',
- 'beginner': '๐ฐ',
- 'recycle': 'โป๏ธ',
- 'u6307': '๐ฏ',
- 'chart': '๐น',
- 'sparkle': 'โ๏ธ',
- 'eight_spoked_asterisk': 'โณ๏ธ',
- 'negative_squared_cross_mark': 'โ',
- 'white_check_mark': 'โ
',
- 'diamond_shape_with_a_dot_inside': '๐ ',
- 'cyclone': '๐',
- 'loop': 'โฟ',
- 'globe_with_meridians': '๐',
- 'm': 'โ๏ธ',
- 'atm': '๐ง',
- 'sa': '๐๏ธ',
- 'passport_control': '๐',
- 'customs': '๐',
- 'baggage_claim': '๐',
- 'left_luggage': '๐
',
- 'wheelchair': 'โฟ',
- 'no_smoking': '๐ญ',
- 'wc': '๐พ',
- 'parking': '๐
ฟ๏ธ',
- 'potable_water': '๐ฐ',
- 'mens': '๐น',
- 'womens': '๐บ',
- 'baby_symbol': '๐ผ',
- 'restroom': '๐ป',
- 'put_litter_in_its_place': '๐ฎ',
- 'cinema': '๐ฆ',
- 'signal_strength': '๐ถ',
- 'koko': '๐',
- 'ng': '๐',
- 'ok': '๐',
- 'up': '๐',
- 'cool': '๐',
- 'new': '๐',
- 'free': '๐',
- 'zero': '0๏ธโฃ',
- 'one': '1๏ธโฃ',
- 'two': '2๏ธโฃ',
- 'three': '3๏ธโฃ',
- 'four': '4๏ธโฃ',
- 'five': '5๏ธโฃ',
- 'six': '6๏ธโฃ',
- 'seven': '7๏ธโฃ',
- 'eight': '8๏ธโฃ',
- 'nine': '9๏ธโฃ',
- 'keycap_ten': '๐',
- 'asterisk': '*โฃ',
- '1234': '๐ข',
- 'eject_button': 'โ๏ธ',
- 'arrow_forward': 'โถ๏ธ',
- 'pause_button': 'โธ',
- 'next_track_button': 'โญ',
- 'stop_button': 'โน',
- 'record_button': 'โบ',
- 'play_or_pause_button': 'โฏ',
- 'previous_track_button': 'โฎ',
- 'fast_forward': 'โฉ',
- 'rewind': 'โช',
- 'twisted_rightwards_arrows': '๐',
- 'repeat': '๐',
- 'repeat_one': '๐',
- 'arrow_backward': 'โ๏ธ',
- 'arrow_up_small': '๐ผ',
- 'arrow_down_small': '๐ฝ',
- 'arrow_double_up': 'โซ',
- 'arrow_double_down': 'โฌ',
- 'arrow_right': 'โก๏ธ',
- 'arrow_left': 'โฌ
๏ธ',
- 'arrow_up': 'โฌ๏ธ',
- 'arrow_down': 'โฌ๏ธ',
- 'arrow_upper_right': 'โ๏ธ',
- 'arrow_lower_right': 'โ๏ธ',
- 'arrow_lower_left': 'โ๏ธ',
- 'arrow_upper_left': 'โ๏ธ',
- 'arrow_up_down': 'โ๏ธ',
- 'left_right_arrow': 'โ๏ธ',
- 'arrows_counterclockwise': '๐',
- 'arrow_right_hook': 'โช๏ธ',
- 'leftwards_arrow_with_hook': 'โฉ๏ธ',
- 'arrow_heading_up': 'โคด๏ธ',
- 'arrow_heading_down': 'โคต๏ธ',
- 'hash': '#๏ธโฃ',
- 'information_source': 'โน๏ธ',
- 'abc': '๐ค',
- 'abcd': '๐ก',
- 'capital_abcd': '๐ ',
- 'symbols': '๐ฃ',
- 'musical_note': '๐ต',
- 'notes': '๐ถ',
- 'wavy_dash': 'ใฐ๏ธ',
- 'curly_loop': 'โฐ',
- 'heavy_check_mark': 'โ๏ธ',
- 'arrows_clockwise': '๐',
- 'heavy_plus_sign': 'โ',
- 'heavy_minus_sign': 'โ',
- 'heavy_division_sign': 'โ',
- 'heavy_multiplication_x': 'โ๏ธ',
- 'heavy_dollar_sign': '๐ฒ',
- 'currency_exchange': '๐ฑ',
- 'copyright': 'ยฉ๏ธ',
- 'registered': 'ยฎ๏ธ',
- 'tm': 'โข๏ธ',
- 'end': '๐',
- 'back': '๐',
- 'on': '๐',
- 'top': '๐',
- 'soon': '๐',
- 'ballot_box_with_check': 'โ๏ธ',
- 'radio_button': '๐',
- 'white_circle': 'โช',
- 'black_circle': 'โซ',
- 'red_circle': '๐ด',
- 'large_blue_circle': '๐ต',
- 'small_orange_diamond': '๐ธ',
- 'small_blue_diamond': '๐น',
- 'large_orange_diamond': '๐ถ',
- 'large_blue_diamond': '๐ท',
- 'small_red_triangle': '๐บ',
- 'black_small_square': 'โช๏ธ',
- 'white_small_square': 'โซ๏ธ',
- 'black_large_square': 'โฌ',
- 'white_large_square': 'โฌ',
- 'small_red_triangle_down': '๐ป',
- 'black_medium_square': 'โผ๏ธ',
- 'white_medium_square': 'โป๏ธ',
- 'black_medium_small_square': 'โพ',
- 'white_medium_small_square': 'โฝ',
- 'black_square_button': '๐ฒ',
- 'white_square_button': '๐ณ',
- 'speaker': '๐',
- 'sound': '๐',
- 'loud_sound': '๐',
- 'mute': '๐',
- 'mega': '๐ฃ',
- 'loudspeaker': '๐ข',
- 'bell': '๐',
- 'no_bell': '๐',
- 'black_joker': '๐',
- 'mahjong': '๐',
- 'spades': 'โ ๏ธ',
- 'clubs': 'โฃ๏ธ',
- 'hearts': 'โฅ๏ธ',
- 'diamonds': 'โฆ๏ธ',
- 'flower_playing_cards': '๐ด',
- 'thought_balloon': '๐ญ',
- 'right_anger_bubble': '๐ฏ',
- 'speech_balloon': '๐ฌ',
- 'left_speech_bubble': '๐จ',
- 'clock1': '๐',
- 'clock2': '๐',
- 'clock3': '๐',
- 'clock4': '๐',
- 'clock5': '๐',
- 'clock6': '๐',
- 'clock7': '๐',
- 'clock8': '๐',
- 'clock9': '๐',
- 'clock10': '๐',
- 'clock11': '๐',
- 'clock12': '๐',
- 'clock130': '๐',
- 'clock230': '๐',
- 'clock330': '๐',
- 'clock430': '๐',
- 'clock530': '๐ ',
- 'clock630': '๐ก',
- 'clock730': '๐ข',
- 'clock830': '๐ฃ',
- 'clock930': '๐ค',
- 'clock1030': '๐ฅ',
- 'clock1130': '๐ฆ',
- 'clock1230': '๐ง',
- 'afghanistan': '๐ฆ๐ซ',
- 'aland_islands': '๐ฆ๐ฝ',
- 'albania': '๐ฆ๐ฑ',
- 'algeria': '๐ฉ๐ฟ',
- 'american_samoa': '๐ฆ๐ธ',
- 'andorra': '๐ฆ๐ฉ',
- 'angola': '๐ฆ๐ด',
- 'anguilla': '๐ฆ๐ฎ',
- 'antarctica': '๐ฆ๐ถ',
- 'antigua_barbuda': '๐ฆ๐ฌ',
- 'argentina': '๐ฆ๐ท',
- 'armenia': '๐ฆ๐ฒ',
- 'aruba': '๐ฆ๐ผ',
- 'australia': '๐ฆ๐บ',
- 'austria': '๐ฆ๐น',
- 'azerbaijan': '๐ฆ๐ฟ',
- 'bahamas': '๐ง๐ธ',
- 'bahrain': '๐ง๐ญ',
- 'bangladesh': '๐ง๐ฉ',
- 'barbados': '๐ง๐ง',
- 'belarus': '๐ง๐พ',
- 'belgium': '๐ง๐ช',
- 'belize': '๐ง๐ฟ',
- 'benin': '๐ง๐ฏ',
- 'bermuda': '๐ง๐ฒ',
- 'bhutan': '๐ง๐น',
- 'bolivia': '๐ง๐ด',
- 'caribbean_netherlands': '๐ง๐ถ',
- 'bosnia_herzegovina': '๐ง๐ฆ',
- 'botswana': '๐ง๐ผ',
- 'brazil': '๐ง๐ท',
- 'british_indian_ocean_territory': '๐ฎ๐ด',
- 'british_virgin_islands': '๐ป๐ฌ',
- 'brunei': '๐ง๐ณ',
- 'bulgaria': '๐ง๐ฌ',
- 'burkina_faso': '๐ง๐ซ',
- 'burundi': '๐ง๐ฎ',
- 'cape_verde': '๐จ๐ป',
- 'cambodia': '๐ฐ๐ญ',
- 'cameroon': '๐จ๐ฒ',
- 'canada': '๐จ๐ฆ',
- 'canary_islands': '๐ฎ๐จ',
- 'cayman_islands': '๐ฐ๐พ',
- 'central_african_republic': '๐จ๐ซ',
- 'chad': '๐น๐ฉ',
- 'chile': '๐จ๐ฑ',
- 'cn': '๐จ๐ณ',
- 'christmas_island': '๐จ๐ฝ',
- 'cocos_islands': '๐จ๐จ',
- 'colombia': '๐จ๐ด',
- 'comoros': '๐ฐ๐ฒ',
- 'congo_brazzaville': '๐จ๐ฌ',
- 'congo_kinshasa': '๐จ๐ฉ',
- 'cook_islands': '๐จ๐ฐ',
- 'costa_rica': '๐จ๐ท',
- 'croatia': '๐ญ๐ท',
- 'cuba': '๐จ๐บ',
- 'curacao': '๐จ๐ผ',
- 'cyprus': '๐จ๐พ',
- 'czech_republic': '๐จ๐ฟ',
- 'denmark': '๐ฉ๐ฐ',
- 'djibouti': '๐ฉ๐ฏ',
- 'dominica': '๐ฉ๐ฒ',
- 'dominican_republic': '๐ฉ๐ด',
- 'ecuador': '๐ช๐จ',
- 'egypt': '๐ช๐ฌ',
- 'el_salvador': '๐ธ๐ป',
- 'equatorial_guinea': '๐ฌ๐ถ',
- 'eritrea': '๐ช๐ท',
- 'estonia': '๐ช๐ช',
- 'ethiopia': '๐ช๐น',
- 'eu': '๐ช๐บ',
- 'falkland_islands': '๐ซ๐ฐ',
- 'faroe_islands': '๐ซ๐ด',
- 'fiji': '๐ซ๐ฏ',
- 'finland': '๐ซ๐ฎ',
- 'fr': '๐ซ๐ท',
- 'french_guiana': '๐ฌ๐ซ',
- 'french_polynesia': '๐ต๐ซ',
- 'french_southern_territories': '๐น๐ซ',
- 'gabon': '๐ฌ๐ฆ',
- 'gambia': '๐ฌ๐ฒ',
- 'georgia': '๐ฌ๐ช',
- 'de': '๐ฉ๐ช',
- 'ghana': '๐ฌ๐ญ',
- 'gibraltar': '๐ฌ๐ฎ',
- 'greece': '๐ฌ๐ท',
- 'greenland': '๐ฌ๐ฑ',
- 'grenada': '๐ฌ๐ฉ',
- 'guadeloupe': '๐ฌ๐ต',
- 'guam': '๐ฌ๐บ',
- 'guatemala': '๐ฌ๐น',
- 'guernsey': '๐ฌ๐ฌ',
- 'guinea': '๐ฌ๐ณ',
- 'guinea_bissau': '๐ฌ๐ผ',
- 'guyana': '๐ฌ๐พ',
- 'haiti': '๐ญ๐น',
- 'honduras': '๐ญ๐ณ',
- 'hong_kong': '๐ญ๐ฐ',
- 'hungary': '๐ญ๐บ',
- 'iceland': '๐ฎ๐ธ',
- 'india': '๐ฎ๐ณ',
- 'indonesia': '๐ฎ๐ฉ',
- 'iran': '๐ฎ๐ท',
- 'iraq': '๐ฎ๐ถ',
- 'ireland': '๐ฎ๐ช',
- 'isle_of_man': '๐ฎ๐ฒ',
- 'israel': '๐ฎ๐ฑ',
- 'it': '๐ฎ๐น',
- 'cote_divoire': '๐จ๐ฎ',
- 'jamaica': '๐ฏ๐ฒ',
- 'jp': '๐ฏ๐ต',
- 'jersey': '๐ฏ๐ช',
- 'jordan': '๐ฏ๐ด',
- 'kazakhstan': '๐ฐ๐ฟ',
- 'kenya': '๐ฐ๐ช',
- 'kiribati': '๐ฐ๐ฎ',
- 'kosovo': '๐ฝ๐ฐ',
- 'kuwait': '๐ฐ๐ผ',
- 'kyrgyzstan': '๐ฐ๐ฌ',
- 'laos': '๐ฑ๐ฆ',
- 'latvia': '๐ฑ๐ป',
- 'lebanon': '๐ฑ๐ง',
- 'lesotho': '๐ฑ๐ธ',
- 'liberia': '๐ฑ๐ท',
- 'libya': '๐ฑ๐พ',
- 'liechtenstein': '๐ฑ๐ฎ',
- 'lithuania': '๐ฑ๐น',
- 'luxembourg': '๐ฑ๐บ',
- 'macau': '๐ฒ๐ด',
- 'macedonia': '๐ฒ๐ฐ',
- 'madagascar': '๐ฒ๐ฌ',
- 'malawi': '๐ฒ๐ผ',
- 'malaysia': '๐ฒ๐พ',
- 'maldives': '๐ฒ๐ป',
- 'mali': '๐ฒ๐ฑ',
- 'malta': '๐ฒ๐น',
- 'marshall_islands': '๐ฒ๐ญ',
- 'martinique': '๐ฒ๐ถ',
- 'mauritania': '๐ฒ๐ท',
- 'mauritius': '๐ฒ๐บ',
- 'mayotte': '๐พ๐น',
- 'mexico': '๐ฒ๐ฝ',
- 'micronesia': '๐ซ๐ฒ',
- 'moldova': '๐ฒ๐ฉ',
- 'monaco': '๐ฒ๐จ',
- 'mongolia': '๐ฒ๐ณ',
- 'montenegro': '๐ฒ๐ช',
- 'montserrat': '๐ฒ๐ธ',
- 'morocco': '๐ฒ๐ฆ',
- 'mozambique': '๐ฒ๐ฟ',
- 'myanmar': '๐ฒ๐ฒ',
- 'namibia': '๐ณ๐ฆ',
- 'nauru': '๐ณ๐ท',
- 'nepal': '๐ณ๐ต',
- 'netherlands': '๐ณ๐ฑ',
- 'new_caledonia': '๐ณ๐จ',
- 'new_zealand': '๐ณ๐ฟ',
- 'nicaragua': '๐ณ๐ฎ',
- 'niger': '๐ณ๐ช',
- 'nigeria': '๐ณ๐ฌ',
- 'niue': '๐ณ๐บ',
- 'norfolk_island': '๐ณ๐ซ',
- 'northern_mariana_islands': '๐ฒ๐ต',
- 'north_korea': '๐ฐ๐ต',
- 'norway': '๐ณ๐ด',
- 'oman': '๐ด๐ฒ',
- 'pakistan': '๐ต๐ฐ',
- 'palau': '๐ต๐ผ',
- 'palestinian_territories': '๐ต๐ธ',
- 'panama': '๐ต๐ฆ',
- 'papua_new_guinea': '๐ต๐ฌ',
- 'paraguay': '๐ต๐พ',
- 'peru': '๐ต๐ช',
- 'philippines': '๐ต๐ญ',
- 'pitcairn_islands': '๐ต๐ณ',
- 'poland': '๐ต๐ฑ',
- 'portugal': '๐ต๐น',
- 'puerto_rico': '๐ต๐ท',
- 'qatar': '๐ถ๐ฆ',
- 'reunion': '๐ท๐ช',
- 'romania': '๐ท๐ด',
- 'ru': '๐ท๐บ',
- 'rwanda': '๐ท๐ผ',
- 'st_barthelemy': '๐ง๐ฑ',
- 'st_helena': '๐ธ๐ญ',
- 'st_kitts_nevis': '๐ฐ๐ณ',
- 'st_lucia': '๐ฑ๐จ',
- 'st_pierre_miquelon': '๐ต๐ฒ',
- 'st_vincent_grenadines': '๐ป๐จ',
- 'samoa': '๐ผ๐ธ',
- 'san_marino': '๐ธ๐ฒ',
- 'sao_tome_principe': '๐ธ๐น',
- 'saudi_arabia': '๐ธ๐ฆ',
- 'senegal': '๐ธ๐ณ',
- 'serbia': '๐ท๐ธ',
- 'seychelles': '๐ธ๐จ',
- 'sierra_leone': '๐ธ๐ฑ',
- 'singapore': '๐ธ๐ฌ',
- 'sint_maarten': '๐ธ๐ฝ',
- 'slovakia': '๐ธ๐ฐ',
- 'slovenia': '๐ธ๐ฎ',
- 'solomon_islands': '๐ธ๐ง',
- 'somalia': '๐ธ๐ด',
- 'south_africa': '๐ฟ๐ฆ',
- 'south_georgia_south_sandwich_islands': '๐ฌ๐ธ',
- 'kr': '๐ฐ๐ท',
- 'south_sudan': '๐ธ๐ธ',
- 'es': '๐ช๐ธ',
- 'sri_lanka': '๐ฑ๐ฐ',
- 'sudan': '๐ธ๐ฉ',
- 'suriname': '๐ธ๐ท',
- 'swaziland': '๐ธ๐ฟ',
- 'sweden': '๐ธ๐ช',
- 'switzerland': '๐จ๐ญ',
- 'syria': '๐ธ๐พ',
- 'taiwan': '๐น๐ผ',
- 'tajikistan': '๐น๐ฏ',
- 'tanzania': '๐น๐ฟ',
- 'thailand': '๐น๐ญ',
- 'timor_leste': '๐น๐ฑ',
- 'togo': '๐น๐ฌ',
- 'tokelau': '๐น๐ฐ',
- 'tonga': '๐น๐ด',
- 'trinidad_tobago': '๐น๐น',
- 'tunisia': '๐น๐ณ',
- 'tr': '๐น๐ท',
- 'turkmenistan': '๐น๐ฒ',
- 'turks_caicos_islands': '๐น๐จ',
- 'tuvalu': '๐น๐ป',
- 'uganda': '๐บ๐ฌ',
- 'ukraine': '๐บ๐ฆ',
- 'united_arab_emirates': '๐ฆ๐ช',
- 'uk': '๐ฌ๐ง',
- 'england': '๐ด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ',
- 'scotland': '๐ด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ',
- 'wales': '๐ด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ',
- 'us': '๐บ๐ธ',
- 'us_virgin_islands': '๐ป๐ฎ',
- 'uruguay': '๐บ๐พ',
- 'uzbekistan': '๐บ๐ฟ',
- 'vanuatu': '๐ป๐บ',
- 'vatican_city': '๐ป๐ฆ',
- 'venezuela': '๐ป๐ช',
- 'vietnam': '๐ป๐ณ',
- 'wallis_futuna': '๐ผ๐ซ',
- 'western_sahara': '๐ช๐ญ',
- 'yemen': '๐พ๐ช',
- 'zambia': '๐ฟ๐ฒ',
- 'zimbabwe': '๐ฟ๐ผ',
-};
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/extension_set.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/extension_set.dart
deleted file mode 100644
index eadda7bc..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/extension_set.dart
+++ /dev/null
@@ -1,64 +0,0 @@
-import 'block_parser.dart';
-import 'inline_parser.dart';
-
-/// ExtensionSets provide a simple grouping mechanism for common Markdown
-/// flavors.
-///
-/// For example, the [gitHubFlavored] set of syntax extensions allows users to
-/// output HTML from their Markdown in a similar fashion to GitHub's parsing.
-class ExtensionSet {
- ExtensionSet(this.blockSyntaxes, this.inlineSyntaxes);
-
- /// The [ExtensionSet.none] extension set renders Markdown similar to
- /// [Markdown.pl].
- ///
- /// However, this set does not render _exactly_ the same as Markdown.pl;
- /// rather it is more-or-less the CommonMark standard of Markdown, without
- /// fenced code blocks, or inline HTML.
- ///
- /// [Markdown.pl]: http://daringfireball.net/projects/markdown/syntax
- static final ExtensionSet none = ExtensionSet([], []);
-
- /// The [commonMark] extension set is close to compliance with [CommonMark].
- ///
- /// [CommonMark]: http://commonmark.org/
- static final ExtensionSet commonMark =
- ExtensionSet([const FencedCodeBlockSyntax()], [InlineHtmlSyntax()]);
-
- /// The [gitHubWeb] extension set renders Markdown similarly to GitHub.
- ///
- /// This is different from the [gitHubFlavored] extension set in that GitHub
- /// actually renders HTML different from straight [GitHub flavored Markdown].
- ///
- /// (The only difference currently is that [gitHubWeb] renders headers with
- /// linkable IDs.)
- ///
- /// [GitHub flavored Markdown]: https://github.github.com/gfm/
- static final ExtensionSet gitHubWeb = ExtensionSet([
- const FencedCodeBlockSyntax(),
- const HeaderWithIdSyntax(),
- const SetextHeaderWithIdSyntax(),
- const TableSyntax()
- ], [
- InlineHtmlSyntax(),
- StrikethroughSyntax(),
- EmojiSyntax(),
- AutolinkExtensionSyntax(),
- ]);
-
- /// The [gitHubFlavored] extension set is close to compliance with the [GitHub
- /// flavored Markdown spec].
- ///
- /// [GitHub flavored Markdown]: https://github.github.com/gfm/
- static final ExtensionSet gitHubFlavored = ExtensionSet([
- const FencedCodeBlockSyntax(),
- const TableSyntax()
- ], [
- InlineHtmlSyntax(),
- StrikethroughSyntax(),
- AutolinkExtensionSyntax(),
- ]);
-
- final List blockSyntaxes;
- final List inlineSyntaxes;
-}
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/html_renderer.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/html_renderer.dart
deleted file mode 100644
index d6630e29..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/html_renderer.dart
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'ast.dart';
-import 'block_parser.dart';
-import 'document.dart';
-import 'extension_set.dart';
-import 'inline_parser.dart';
-
-/// Converts the given string of Markdown to HTML.
-String markdownToHtml(String markdown,
- {Iterable? blockSyntaxes,
- Iterable? inlineSyntaxes,
- ExtensionSet? extensionSet,
- Resolver? linkResolver,
- Resolver? imageLinkResolver,
- bool inlineOnly = false}) {
- final document = Document(
- blockSyntaxes: blockSyntaxes,
- inlineSyntaxes: inlineSyntaxes,
- extensionSet: extensionSet,
- linkResolver: linkResolver,
- imageLinkResolver: imageLinkResolver);
-
- if (inlineOnly) {
- return renderToHtml(document.parseInline(markdown)!);
- }
-
- // Replace windows line endings with unix line endings, and split.
- final lines = markdown.replaceAll('\r\n', '\n').split('\n');
-
- return '${renderToHtml(document.parseLines(lines))}\n';
-}
-
-/// Renders [nodes] to HTML.
-String renderToHtml(List nodes) => HtmlRenderer().render(nodes);
-
-/// Translates a parsed AST to HTML.
-class HtmlRenderer implements NodeVisitor {
- HtmlRenderer();
-
- static final _blockTags = RegExp('blockquote|h1|h2|h3|h4|h5|h6|hr|p|pre');
-
- late StringBuffer buffer;
- late Set uniqueIds;
-
- String render(List nodes) {
- buffer = StringBuffer();
- uniqueIds = {};
-
- for (final node in nodes) {
- node.accept(this);
- }
-
- return buffer.toString();
- }
-
- @override
- void visitText(Text text) {
- buffer.write(text.text);
- }
-
- @override
- bool visitElementBefore(Element element) {
- // Hackish. Separate block-level elements with newlines.
- if (buffer.isNotEmpty && _blockTags.firstMatch(element.tag) != null) {
- buffer.write('\n');
- }
-
- buffer.write('<${element.tag}');
-
- // Sort the keys so that we generate stable output.
- final attributeNames = element.attributes.keys.toList()
- ..sort((a, b) => a.compareTo(b));
-
- for (final name in attributeNames) {
- buffer.write(' $name="${element.attributes[name]}"');
- }
-
- // attach header anchor ids generated from text
- if (element.generatedId != null) {
- buffer.write(' id="${uniquifyId(element.generatedId!)}"');
- }
-
- if (element.isEmpty) {
- // Empty element like .
- buffer.write(' />');
-
- if (element.tag == 'br') {
- buffer.write('\n');
- }
-
- return false;
- } else {
- buffer.write('>');
- return true;
- }
- }
-
- @override
- void visitElementAfter(Element element) {
- buffer.write('${element.tag}>');
- }
-
- /// Uniquifies an id generated from text.
- String uniquifyId(String id) {
- if (!uniqueIds.contains(id)) {
- uniqueIds.add(id);
- return id;
- }
-
- var suffix = 2;
- var suffixedId = '$id-$suffix';
- while (uniqueIds.contains(suffixedId)) {
- suffixedId = '$id-${suffix++}';
- }
- uniqueIds.add(suffixedId);
- return suffixedId;
- }
-}
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/inline_parser.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/inline_parser.dart
deleted file mode 100644
index ecbc9a16..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/inline_parser.dart
+++ /dev/null
@@ -1,1266 +0,0 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:charcode/charcode.dart';
-
-import 'ast.dart';
-import 'document.dart';
-import 'emojis.dart';
-import 'util.dart';
-
-/// Maintains the internal state needed to parse inline span elements in
-/// Markdown.
-class InlineParser {
- InlineParser(this.source, this.document) : _stack = [] {
- // User specified syntaxes are the first syntaxes to be evaluated.
- syntaxes.addAll(document.inlineSyntaxes);
-
- final documentHasCustomInlineSyntaxes = document.inlineSyntaxes
- .any((s) => !document.extensionSet.inlineSyntaxes.contains(s));
-
- // This first RegExp matches plain text to accelerate parsing. It's written
- // so that it does not match any prefix of any following syntaxes. Most
- // Markdown is plain text, so it's faster to match one RegExp per 'word'
- // rather than fail to match all the following RegExps at each non-syntax
- // character position.
- if (documentHasCustomInlineSyntaxes) {
- // We should be less aggressive in blowing past "words".
- syntaxes.add(TextSyntax(r'[A-Za-z0-9]+(?=\s)'));
- } else {
- syntaxes.add(TextSyntax(r'[ \tA-Za-z0-9]*[A-Za-z0-9](?=\s)'));
- }
-
- syntaxes
- ..addAll(_defaultSyntaxes)
- // Custom link resolvers go after the generic text syntax.
- ..insertAll(1, [
- LinkSyntax(linkResolver: document.linkResolver),
- ImageSyntax(linkResolver: document.imageLinkResolver)
- ]);
- }
-
- static final List _defaultSyntaxes =
- List.unmodifiable([
- EmailAutolinkSyntax(),
- AutolinkSyntax(),
- LineBreakSyntax(),
- LinkSyntax(),
- ImageSyntax(),
- // Allow any punctuation to be escaped.
- EscapeSyntax(),
- // "*" surrounded by spaces is left alone.
- TextSyntax(r' \* '),
- // "_" surrounded by spaces is left alone.
- TextSyntax(r' _ '),
- // Parse "**strong**" and "*emphasis*" tags.
- TagSyntax(r'\*+', requiresDelimiterRun: true),
- // Parse "__strong__" and "_emphasis_" tags.
- TagSyntax(r'_+', requiresDelimiterRun: true),
- CodeSyntax(),
- // We will add the LinkSyntax once we know about the specific link resolver.
- ]);
-
- /// The string of Markdown being parsed.
- final String source;
-
- /// The Markdown document this parser is parsing.
- final Document document;
-
- final List syntaxes = [];
-
- /// The current read position.
- int pos = 0;
-
- /// Starting position of the last unconsumed text.
- int start = 0;
-
- final List _stack;
-
- List? parse() {
- // Make a fake top tag to hold the results.
- _stack.add(TagState(0, 0, null, null));
-
- while (!isDone) {
- // See if any of the current tags on the stack match. This takes
- // priority over other possible matches.
- if (_stack.reversed
- .any((state) => state.syntax != null && state.tryMatch(this))) {
- continue;
- }
-
- // See if the current text matches any defined markdown syntax.
- if (syntaxes.any((syntax) => syntax.tryMatch(this))) {
- continue;
- }
-
- // If we got here, it's just text.
- advanceBy(1);
- }
-
- // Unwind any unmatched tags and get the results.
- return _stack[0].close(this, null);
- }
-
- int charAt(int index) => source.codeUnitAt(index);
-
- void writeText() {
- writeTextRange(start, pos);
- start = pos;
- }
-
- void writeTextRange(int start, int end) {
- if (end <= start) {
- return;
- }
-
- final text = source.substring(start, end);
- final nodes = _stack.last.children;
-
- // If the previous node is text too, just append.
- if (nodes.isNotEmpty && nodes.last is Text) {
- final textNode = nodes.last as Text;
- nodes[nodes.length - 1] = Text('${textNode.text}$text');
- } else {
- nodes.add(Text(text));
- }
- }
-
- /// Add [node] to the last [TagState] on the stack.
- void addNode(Node node) {
- _stack.last.children.add(node);
- }
-
- /// Push [state] onto the stack of [TagState]s.
- void openTag(TagState state) => _stack.add(state);
-
- bool get isDone => pos == source.length;
-
- void advanceBy(int length) {
- pos += length;
- }
-
- void consume(int length) {
- pos += length;
- start = pos;
- }
-}
-
-/// Represents one kind of Markdown tag that can be parsed.
-abstract class InlineSyntax {
- InlineSyntax(String pattern) : pattern = RegExp(pattern, multiLine: true);
-
- final RegExp pattern;
-
- /// Tries to match at the parser's current position.
- ///
- /// The parser's position can be overriden with [startMatchPos].
- /// Returns whether or not the pattern successfully matched.
- bool tryMatch(InlineParser parser, [int? startMatchPos]) {
- startMatchPos ??= parser.pos;
-
- final startMatch = pattern.matchAsPrefix(parser.source, startMatchPos);
- if (startMatch == null) {
- return false;
- }
-
- // Write any existing plain text up to this point.
- parser.writeText();
-
- if (onMatch(parser, startMatch)) {
- parser.consume(startMatch[0]!.length);
- }
- return true;
- }
-
- /// Processes [match], adding nodes to [parser] and possibly advancing
- /// [parser].
- ///
- /// Returns whether the caller should advance [parser] by `match[0].length`.
- bool onMatch(InlineParser parser, Match match);
-}
-
-/// Represents a hard line break.
-class LineBreakSyntax extends InlineSyntax {
- LineBreakSyntax() : super(r'(?:\\| +)\n');
-
- /// Create a void element.
- @override
- bool onMatch(InlineParser parser, Match match) {
- parser.addNode(Element.empty('br'));
- return true;
- }
-}
-
-/// Matches stuff that should just be passed through as straight text.
-class TextSyntax extends InlineSyntax {
- TextSyntax(super.pattern, {String? sub}) : substitute = sub;
-
- final String? substitute;
-
- @override
- bool onMatch(InlineParser parser, Match match) {
- if (substitute == null) {
- // Just use the original matched text.
- parser.advanceBy(match[0]!.length);
- return false;
- }
-
- // Insert the substitution.
- parser.addNode(Text(substitute!));
- return true;
- }
-}
-
-/// Escape punctuation preceded by a backslash.
-class EscapeSyntax extends InlineSyntax {
- EscapeSyntax() : super(r'''\\[!"#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~]''');
-
- @override
- bool onMatch(InlineParser parser, Match match) {
- // Insert the substitution.
- parser.addNode(Text(match[0]![1]));
- return true;
- }
-}
-
-/// Leave inline HTML tags alone, from
-/// [CommonMark 0.28](http://spec.commonmark.org/0.28/#raw-html).
-///
-/// This is not actually a good definition (nor CommonMark's) of an HTML tag,
-/// but it is fast. It will leave text like `]*)?>');
-}
-
-/// Matches autolinks like ``.
-///
-/// See .
-class EmailAutolinkSyntax extends InlineSyntax {
- EmailAutolinkSyntax() : super('<($_email)>');
-
- static const _email =
- r'''[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}'''
- r'''[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*''';
-
- @override
- bool onMatch(InlineParser parser, Match match) {
- final url = match[1]!;
- final anchor = Element.text('a', escapeHtml(url));
- anchor.attributes['href'] = Uri.encodeFull('mailto:$url');
- parser.addNode(anchor);
-
- return true;
- }
-}
-
-/// Matches autolinks like ``.
-class AutolinkSyntax extends InlineSyntax {
- AutolinkSyntax() : super(r'<(([a-zA-Z][a-zA-Z\-\+\.]+):(?://)?[^\s>]*)>');
-
- @override
- bool onMatch(InlineParser parser, Match match) {
- final url = match[1]!;
- final anchor = Element.text('a', escapeHtml(url));
- anchor.attributes['href'] = Uri.encodeFull(url);
- parser.addNode(anchor);
-
- return true;
- }
-}
-
-/// Matches autolinks like `http://foo.com`.
-class AutolinkExtensionSyntax extends InlineSyntax {
- AutolinkExtensionSyntax() : super('$start(($scheme)($domain)($path))');
-
- /// Broken up parts of the autolink regex for reusability and readability
-
- // Autolinks can only come at the beginning of a line, after whitespace, or
- // any of the delimiting characters *, _, ~, and (.
- static const start = r'(?:^|[\s*_~(>])';
- // An extended url autolink will be recognized when one of the schemes
- // http://, https://, or ftp://, followed by a valid domain
- static const scheme = r'(?:(?:https?|ftp):\/\/|www\.)';
- // A valid domain consists of alphanumeric characters, underscores (_),
- // hyphens (-) and periods (.). There must be at least one period, and no
- // underscores may be present in the last two segments of the domain.
- static const domainPart = r'\w\-';
- static const domain = '[$domainPart][$domainPart.]+';
- // A valid domain consists of alphanumeric characters, underscores (_),
- // hyphens (-) and periods (.).
- static const path = r'[^\s<]*';
- // Trailing punctuation (specifically, ?, !, ., ,, :, *, _, and ~) will not
- // be considered part of the autolink
- static const truncatingPunctuationPositive = r'[?!.,:*_~]';
-
- static final regExpTrailingPunc =
- RegExp('$truncatingPunctuationPositive*' r'$');
- static final regExpEndsWithColon = RegExp(r'\&[a-zA-Z0-9]+;$');
- static final regExpWhiteSpace = RegExp(r'\s');
-
- @override
- bool tryMatch(InlineParser parser, [int? startMatchPos]) {
- return super.tryMatch(parser, parser.pos > 0 ? parser.pos - 1 : 0);
- }
-
- @override
- bool onMatch(InlineParser parser, Match match) {
- var url = match[1]!;
- var href = url;
- var matchLength = url.length;
-
- if (url[0] == '>' || url.startsWith(regExpWhiteSpace)) {
- url = url.substring(1, url.length - 1);
- href = href.substring(1, href.length - 1);
- parser.pos++;
- matchLength--;
- }
-
- // Prevent accidental standard autolink matches
- if (url.endsWith('>') && parser.source[parser.pos - 1] == '<') {
- return false;
- }
-
- // When an autolink ends in ), we scan the entire autolink for the total
- // number of parentheses. If there is a greater number of closing
- // parentheses than opening ones, we donโt consider the last character
- // part of the autolink, in order to facilitate including an autolink
- // inside a parenthesis:
- // https://github.github.com/gfm/#example-600
- if (url.endsWith(')')) {
- final opening = _countChars(url, '(');
- final closing = _countChars(url, ')');
-
- if (closing > opening) {
- url = url.substring(0, url.length - 1);
- href = href.substring(0, href.length - 1);
- matchLength--;
- }
- }
-
- // Trailing punctuation (specifically, ?, !, ., ,, :, *, _, and ~) will
- // not be considered part of the autolink, though they may be included
- // in the interior of the link:
- // https://github.github.com/gfm/#example-599
- final trailingPunc = regExpTrailingPunc.firstMatch(url);
- if (trailingPunc != null) {
- url = url.substring(0, url.length - trailingPunc[0]!.length);
- href = href.substring(0, href.length - trailingPunc[0]!.length);
- matchLength -= trailingPunc[0]!.length;
- }
-
- // If an autolink ends in a semicolon (;), we check to see if it appears
- // to resemble an
- // [entity reference](https://github.github.com/gfm/#entity-references);
- // if the preceding text is & followed by one or more alphanumeric
- // characters. If so, it is excluded from the autolink:
- // https://github.github.com/gfm/#example-602
- if (url.endsWith(';')) {
- final entityRef = regExpEndsWithColon.firstMatch(url);
- if (entityRef != null) {
- // Strip out HTML entity reference
- url = url.substring(0, url.length - entityRef[0]!.length);
- href = href.substring(0, href.length - entityRef[0]!.length);
- matchLength -= entityRef[0]!.length;
- }
- }
-
- // The scheme http will be inserted automatically
- if (!href.startsWith('http://') &&
- !href.startsWith('https://') &&
- !href.startsWith('ftp://')) {
- href = 'http://$href';
- }
-
- final anchor = Element.text('a', escapeHtml(url));
- anchor.attributes['href'] = Uri.encodeFull(href);
- parser
- ..addNode(anchor)
- ..consume(matchLength);
- return false;
- }
-
- int _countChars(String input, String char) {
- var count = 0;
-
- for (var i = 0; i < input.length; i++) {
- if (input[i] == char) {
- count++;
- }
- }
-
- return count;
- }
-}
-
-class DelimiterRun {
- DelimiterRun._(
- {this.char,
- this.length,
- this.isLeftFlanking,
- this.isRightFlanking,
- this.isPrecededByPunctuation,
- this.isFollowedByPunctuation});
-
- static const String punctuation = r'''!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~''';
- // TODO(srawlins): Unicode whitespace
- static const String whitespace = ' \t\r\n';
-
- final int? char;
- final int? length;
- final bool? isLeftFlanking;
- final bool? isRightFlanking;
- final bool? isPrecededByPunctuation;
- final bool? isFollowedByPunctuation;
-
- // ignore: prefer_constructors_over_static_methods
- static DelimiterRun? tryParse(InlineParser parser, int runStart, int runEnd) {
- bool leftFlanking,
- rightFlanking,
- precededByPunctuation,
- followedByPunctuation;
- String preceding, following;
- if (runStart == 0) {
- rightFlanking = false;
- preceding = '\n';
- } else {
- preceding = parser.source.substring(runStart - 1, runStart);
- }
- precededByPunctuation = punctuation.contains(preceding);
-
- if (runEnd == parser.source.length - 1) {
- leftFlanking = false;
- following = '\n';
- } else {
- following = parser.source.substring(runEnd + 1, runEnd + 2);
- }
- followedByPunctuation = punctuation.contains(following);
-
- // http://spec.commonmark.org/0.28/#left-flanking-delimiter-run
- if (whitespace.contains(following)) {
- leftFlanking = false;
- } else {
- leftFlanking = !followedByPunctuation ||
- whitespace.contains(preceding) ||
- precededByPunctuation;
- }
-
- // http://spec.commonmark.org/0.28/#right-flanking-delimiter-run
- if (whitespace.contains(preceding)) {
- rightFlanking = false;
- } else {
- rightFlanking = !precededByPunctuation ||
- whitespace.contains(following) ||
- followedByPunctuation;
- }
-
- if (!leftFlanking && !rightFlanking) {
- // Could not parse a delimiter run.
- return null;
- }
-
- return DelimiterRun._(
- char: parser.charAt(runStart),
- length: runEnd - runStart + 1,
- isLeftFlanking: leftFlanking,
- isRightFlanking: rightFlanking,
- isPrecededByPunctuation: precededByPunctuation,
- isFollowedByPunctuation: followedByPunctuation);
- }
-
- @override
- String toString() =>
- '';
-
- // Whether a delimiter in this run can open emphasis or strong emphasis.
- bool get canOpen =>
- isLeftFlanking! &&
- (char == $asterisk || !isRightFlanking! || isPrecededByPunctuation!);
-
- // Whether a delimiter in this run can close emphasis or strong emphasis.
- bool get canClose =>
- isRightFlanking! &&
- (char == $asterisk || !isLeftFlanking! || isFollowedByPunctuation!);
-}
-
-/// Matches syntax that has a pair of tags and becomes an element, like `*` for
-/// ``. Allows nested tags.
-class TagSyntax extends InlineSyntax {
- TagSyntax(super.pattern, {String? end, this.requiresDelimiterRun = false})
- : endPattern = RegExp((end != null) ? end : pattern, multiLine: true);
-
- final RegExp endPattern;
-
- /// Whether this is parsed according to the same nesting rules as [emphasis
- /// delimiters][].
- ///
- /// [emphasis delimiters]: http://spec.commonmark.org/0.28/#can-open-emphasis
- final bool requiresDelimiterRun;
-
- @override
- bool onMatch(InlineParser parser, Match match) {
- final runLength = match.group(0)!.length;
- final matchStart = parser.pos;
- final matchEnd = parser.pos + runLength - 1;
- if (!requiresDelimiterRun) {
- parser.openTag(TagState(parser.pos, matchEnd + 1, this, null));
- return true;
- }
-
- final delimiterRun = DelimiterRun.tryParse(parser, matchStart, matchEnd);
- if (delimiterRun != null && delimiterRun.canOpen) {
- parser.openTag(TagState(parser.pos, matchEnd + 1, this, delimiterRun));
- return true;
- } else {
- parser.advanceBy(runLength);
- return false;
- }
- }
-
- bool onMatchEnd(InlineParser parser, Match match, TagState state) {
- final runLength = match.group(0)!.length;
- final matchStart = parser.pos;
- final matchEnd = parser.pos + runLength - 1;
- final openingRunLength = state.endPos - state.startPos;
- final delimiterRun = DelimiterRun.tryParse(parser, matchStart, matchEnd);
-
- if (openingRunLength == 1 && runLength == 1) {
- parser.addNode(Element('em', state.children));
- } else if (openingRunLength == 1 && runLength > 1) {
- parser
- ..addNode(Element('em', state.children))
- ..pos = parser.pos - (runLength - 1)
- ..start = parser.pos;
- } else if (openingRunLength > 1 && runLength == 1) {
- parser
- ..openTag(
- TagState(state.startPos, state.endPos - 1, this, delimiterRun))
- ..addNode(Element('em', state.children));
- } else if (openingRunLength == 2 && runLength == 2) {
- parser.addNode(Element('strong', state.children));
- } else if (openingRunLength == 2 && runLength > 2) {
- parser
- ..addNode(Element('strong', state.children))
- ..pos = parser.pos - (runLength - 2)
- ..start = parser.pos;
- } else if (openingRunLength > 2 && runLength == 2) {
- parser
- ..openTag(
- TagState(state.startPos, state.endPos - 2, this, delimiterRun))
- ..addNode(Element('strong', state.children));
- } else if (openingRunLength > 2 && runLength > 2) {
- parser
- ..openTag(
- TagState(state.startPos, state.endPos - 2, this, delimiterRun))
- ..addNode(Element('strong', state.children))
- ..pos = parser.pos - (runLength - 2)
- ..start = parser.pos;
- }
-
- return true;
- }
-}
-
-/// Matches strikethrough syntax according to the GFM spec.
-class StrikethroughSyntax extends TagSyntax {
- StrikethroughSyntax() : super('~+', requiresDelimiterRun: true);
-
- @override
- bool onMatchEnd(InlineParser parser, Match match, TagState state) {
- final runLength = match.group(0)!.length;
- final matchStart = parser.pos;
- final matchEnd = parser.pos + runLength - 1;
- final delimiterRun = DelimiterRun.tryParse(parser, matchStart, matchEnd)!;
- if (!delimiterRun.isRightFlanking!) {
- return false;
- }
-
- parser.addNode(Element('del', state.children));
- return true;
- }
-}
-
-/// Matches links like `[blah][label]` and `[blah](url)`.
-class LinkSyntax extends TagSyntax {
- LinkSyntax({Resolver? linkResolver, String pattern = r'\['})
- : linkResolver = (linkResolver ?? (_, [__]) => null),
- super(pattern, end: r'\]');
-
- static final _entirelyWhitespacePattern = RegExp(r'^\s*$');
-
- final Resolver linkResolver;
-
- // The pending [TagState]s, all together, are "active" or "inactive" based on
- // whether a link element has just been parsed.
- //
- // Links cannot be nested, so we must "deactivate" any pending ones. For
- // example, take the following text:
- //
- // Text [link and [more](links)](links).
- //
- // Once we have parsed `Text [`, there is one (pending) link in the state
- // stack. It is, by default, active. Once we parse the next possible link,
- // `[more](links)`, as a real link, we must deactive the pending links (just
- // the one, in this case).
- var _pendingStatesAreActive = true;
-
- @override
- bool onMatch(InlineParser parser, Match match) {
- final matched = super.onMatch(parser, match);
- if (!matched) {
- return false;
- }
-
- _pendingStatesAreActive = true;
-
- return true;
- }
-
- @override
- bool onMatchEnd(InlineParser parser, Match match, TagState state) {
- if (!_pendingStatesAreActive) {
- return false;
- }
-
- final text = parser.source.substring(state.endPos, parser.pos);
- // The current character is the `]` that closed the link text. Examine the
- // next character, to determine what type of link we might have (a '('
- // means a possible inline link; otherwise a possible reference link).
- if (parser.pos + 1 >= parser.source.length) {
- // In this case, the Markdown document may have ended with a shortcut
- // reference link.
-
- return _tryAddReferenceLink(parser, state, text);
- }
- // Peek at the next character; don't advance, so as to avoid later stepping
- // backward.
- final char = parser.charAt(parser.pos + 1);
-
- if (char == $lparen) {
- // Maybe an inline link, like `[text](destination)`.
- parser.advanceBy(1);
- final leftParenIndex = parser.pos;
- final inlineLink = _parseInlineLink(parser);
- if (inlineLink != null) {
- return _tryAddInlineLink(parser, state, inlineLink);
- }
-
- // Reset the parser position.
- parser
- ..pos = leftParenIndex
-
- // At this point, we've matched `[...](`, but that `(` did not pan out
- // to be an inline link. We must now check if `[...]` is simply a
- // shortcut reference link.
- ..advanceBy(-1);
- return _tryAddReferenceLink(parser, state, text);
- }
-
- if (char == $lbracket) {
- parser.advanceBy(1);
- // At this point, we've matched `[...][`. Maybe a *full* reference link,
- // like `[foo][bar]` or a *collapsed* reference link, like `[foo][]`.
- if (parser.pos + 1 < parser.source.length &&
- parser.charAt(parser.pos + 1) == $rbracket) {
- // That opening `[` is not actually part of the link. Maybe a
- // *shortcut* reference link (followed by a `[`).
- parser.advanceBy(1);
- return _tryAddReferenceLink(parser, state, text);
- }
- final label = _parseReferenceLinkLabel(parser);
- if (label != null) {
- return _tryAddReferenceLink(parser, state, label);
- }
- return false;
- }
-
- // The link text (inside `[...]`) was not followed with a opening `(` nor
- // an opening `[`. Perhaps just a simple shortcut reference link (`[...]`).
-
- return _tryAddReferenceLink(parser, state, text);
- }
-
- /// Resolve a possible reference link.
- ///
- /// Uses [linkReferences], [linkResolver], and [_createNode] to try to
- /// resolve [label] and [state] into a [Node]. If [label] is defined in
- /// [linkReferences] or can be resolved by [linkResolver], returns a [Node]
- /// that links to the resolved URL.
- ///
- /// Otherwise, returns `null`.
- ///
- /// [label] does not need to be normalized.
- Node? _resolveReferenceLink(
- String label, TagState state, Map linkReferences) {
- final normalizedLabel = label.toLowerCase();
- final linkReference = linkReferences[normalizedLabel];
- if (linkReference != null) {
- return _createNode(state, linkReference.destination, linkReference.title);
- } else {
- // This link has no reference definition. But we allow users of the
- // library to specify a custom resolver function ([linkResolver]) that
- // may choose to handle this. Otherwise, it's just treated as plain
- // text.
-
- // Normally, label text does not get parsed as inline Markdown. However,
- // for the benefit of the link resolver, we need to at least escape
- // brackets, so that, e.g. a link resolver can receive `[\[\]]` as `[]`.
- return linkResolver(label
- .replaceAll(r'\\', r'\')
- .replaceAll(r'\[', '[')
- .replaceAll(r'\]', ']'));
- }
- }
-
- /// Create the node represented by a Markdown link.
- Node _createNode(TagState state, String destination, String? title) {
- final element = Element('a', state.children);
- element.attributes['href'] = escapeAttribute(destination);
- if (title != null && title.isNotEmpty) {
- element.attributes['title'] = escapeAttribute(title);
- }
- return element;
- }
-
- // Add a reference link node to [parser]'s AST.
- //
- // Returns whether the link was added successfully.
- bool _tryAddReferenceLink(InlineParser parser, TagState state, String label) {
- final element =
- _resolveReferenceLink(label, state, parser.document.linkReferences);
- if (element == null) {
- return false;
- }
- parser
- ..addNode(element)
- ..start = parser.pos;
- _pendingStatesAreActive = false;
- return true;
- }
-
- // Add an inline link node to [parser]'s AST.
- //
- // Returns whether the link was added successfully.
- bool _tryAddInlineLink(InlineParser parser, TagState state, InlineLink link) {
- final element = _createNode(state, link.destination, link.title);
- parser
- ..addNode(element)
- ..start = parser.pos;
- _pendingStatesAreActive = false;
- return true;
- }
-
- /// Parse a reference link label at the current position.
- ///
- /// Specifically, [parser.pos] is expected to be pointing at the `[` which
- /// opens the link label.
- ///
- /// Returns the label if it could be parsed, or `null` if not.
- String? _parseReferenceLinkLabel(InlineParser parser) {
- // Walk past the opening `[`.
- parser.advanceBy(1);
- if (parser.isDone) {
- return null;
- }
-
- final buffer = StringBuffer();
- while (true) {
- final char = parser.charAt(parser.pos);
- if (char == $backslash) {
- parser.advanceBy(1);
- final next = parser.charAt(parser.pos);
- if (next != $backslash && next != $rbracket) {
- buffer.writeCharCode(char);
- }
- buffer.writeCharCode(next);
- } else if (char == $rbracket) {
- break;
- } else {
- buffer.writeCharCode(char);
- }
- parser.advanceBy(1);
- if (parser.isDone) {
- return null;
- }
- // TODO(srawlins): only check 999 characters, for performance reasons?
- }
-
- final label = buffer.toString();
-
- // A link label must contain at least one non-whitespace character.
- if (_entirelyWhitespacePattern.hasMatch(label)) {
- return null;
- }
-
- return label;
- }
-
- /// Parse an inline [InlineLink] at the current position.
- ///
- /// At this point, we have parsed a link's (or image's) opening `[`, and then
- /// a matching closing `]`, and [parser.pos] is pointing at an opening `(`.
- /// This method will then attempt to parse a link destination wrapped in `<>`,
- /// such as `()`, or a bare link destination, such as
- /// `(http://url)`, or a link destination with a title, such as
- /// `(http://url "title")`.
- ///
- /// Returns the [InlineLink] if one was parsed, or `null` if not.
- InlineLink? _parseInlineLink(InlineParser parser) {
- // Start walking to the character just after the opening `(`.
- parser.advanceBy(1);
-
- _moveThroughWhitespace(parser);
- if (parser.isDone) {
- return null; // EOF. Not a link.
- }
-
- if (parser.charAt(parser.pos) == $lt) {
- // Maybe a `<...>`-enclosed link destination.
- return _parseInlineBracketedLink(parser);
- } else {
- return _parseInlineBareDestinationLink(parser);
- }
- }
-
- /// Parse an inline link with a bracketed destination (a destination wrapped
- /// in `<...>`). The current position of the parser must be the first
- /// character of the destination.
- InlineLink? _parseInlineBracketedLink(InlineParser parser) {
- parser.advanceBy(1);
-
- final buffer = StringBuffer();
- while (true) {
- final char = parser.charAt(parser.pos);
- if (char == $backslash) {
- parser.advanceBy(1);
- final next = parser.charAt(parser.pos);
- if (char == $space || char == $lf || char == $cr || char == $ff) {
- // Not a link (no whitespace allowed within `<...>`).
- return null;
- }
- // TODO: Follow the backslash spec better here.
- // http://spec.commonmark.org/0.28/#backslash-escapes
- if (next != $backslash && next != $gt) {
- buffer.writeCharCode(char);
- }
- buffer.writeCharCode(next);
- } else if (char == $space || char == $lf || char == $cr || char == $ff) {
- // Not a link (no whitespace allowed within `<...>`).
- return null;
- } else if (char == $gt) {
- break;
- } else {
- buffer.writeCharCode(char);
- }
- parser.advanceBy(1);
- if (parser.isDone) {
- return null;
- }
- }
- final destination = buffer.toString();
-
- parser.advanceBy(1);
- final char = parser.charAt(parser.pos);
- if (char == $space || char == $lf || char == $cr || char == $ff) {
- final title = _parseTitle(parser);
- if (title == null && parser.charAt(parser.pos) != $rparen) {
- // This looked like an inline link, until we found this $space
- // followed by mystery characters; no longer a link.
- return null;
- }
- return InlineLink(destination, title: title);
- } else if (char == $rparen) {
- return InlineLink(destination);
- } else {
- // We parsed something like `[foo](X`. Not a link.
- return null;
- }
- }
-
- /// Parse an inline link with a "bare" destination (a destination _not_
- /// wrapped in `<...>`). The current position of the parser must be the first
- /// character of the destination.
- InlineLink? _parseInlineBareDestinationLink(InlineParser parser) {
- // According to
- // [CommonMark](http://spec.commonmark.org/0.28/#link-destination):
- //
- // > A link destination consists of [...] a nonempty sequence of
- // > characters [...], and includes parentheses only if (a) they are
- // > backslash-escaped or (b) they are part of a balanced pair of
- // > unescaped parentheses.
- //
- // We need to count the open parens. We start with 1 for the paren that
- // opened the destination.
- var parenCount = 1;
- final buffer = StringBuffer();
-
- while (true) {
- final char = parser.charAt(parser.pos);
- switch (char) {
- case $backslash:
- parser.advanceBy(1);
- if (parser.isDone) {
- return null; // EOF. Not a link.
- }
-
- final next = parser.charAt(parser.pos);
- // Parentheses may be escaped.
- //
- // http://spec.commonmark.org/0.28/#example-467
- if (next != $backslash && next != $lparen && next != $rparen) {
- buffer.writeCharCode(char);
- }
- buffer.writeCharCode(next);
- break;
-
- case $space:
- case $lf:
- case $cr:
- case $ff:
- final destination = buffer.toString();
- final title = _parseTitle(parser);
- if (title == null && parser.charAt(parser.pos) != $rparen) {
- // This looked like an inline link, until we found this $space
- // followed by mystery characters; no longer a link.
- return null;
- }
- // [_parseTitle] made sure the title was follwed by a closing `)`
- // (but it's up to the code here to examine the balance of
- // parentheses).
- parenCount--;
- if (parenCount == 0) {
- return InlineLink(destination, title: title);
- }
- break;
-
- case $lparen:
- parenCount++;
- buffer.writeCharCode(char);
- break;
-
- case $rparen:
- parenCount--;
- // ignore: invariant_booleans
- if (parenCount == 0) {
- final destination = buffer.toString();
- return InlineLink(destination);
- }
- buffer.writeCharCode(char);
- break;
-
- default:
- buffer.writeCharCode(char);
- }
- parser.advanceBy(1);
- if (parser.isDone) {
- return null; // EOF. Not a link.
- }
- }
- }
-
- // Walk the parser forward through any whitespace.
- void _moveThroughWhitespace(InlineParser parser) {
- while (true) {
- final char = parser.charAt(parser.pos);
- if (char != $space &&
- char != $tab &&
- char != $lf &&
- char != $vt &&
- char != $cr &&
- char != $ff) {
- return;
- }
- parser.advanceBy(1);
- if (parser.isDone) {
- return;
- }
- }
- }
-
- // Parse a link title in [parser] at it's current position. The parser's
- // current position should be a whitespace character that followed a link
- // destination.
- String? _parseTitle(InlineParser parser) {
- _moveThroughWhitespace(parser);
- if (parser.isDone) {
- return null;
- }
-
- // The whitespace should be followed by a title delimiter.
- final delimiter = parser.charAt(parser.pos);
- if (delimiter != $apostrophe &&
- delimiter != $quote &&
- delimiter != $lparen) {
- return null;
- }
-
- final closeDelimiter = delimiter == $lparen ? $rparen : delimiter;
- parser.advanceBy(1);
-
- // Now we look for an un-escaped closing delimiter.
- final buffer = StringBuffer();
- while (true) {
- final char = parser.charAt(parser.pos);
- if (char == $backslash) {
- parser.advanceBy(1);
- final next = parser.charAt(parser.pos);
- if (next != $backslash && next != closeDelimiter) {
- buffer.writeCharCode(char);
- }
- buffer.writeCharCode(next);
- } else if (char == closeDelimiter) {
- break;
- } else {
- buffer.writeCharCode(char);
- }
- parser.advanceBy(1);
- if (parser.isDone) {
- return null;
- }
- }
- final title = buffer.toString();
-
- // Advance past the closing delimiter.
- parser.advanceBy(1);
- if (parser.isDone) {
- return null;
- }
- _moveThroughWhitespace(parser);
- if (parser.isDone) {
- return null;
- }
- if (parser.charAt(parser.pos) != $rparen) {
- return null;
- }
- return title;
- }
-}
-
-/// Matches images like `` and
-/// `![alternate text][label]`.
-class ImageSyntax extends LinkSyntax {
- ImageSyntax({super.linkResolver}) : super(pattern: r'!\[');
-
- @override
- Node _createNode(TagState state, String destination, String? title) {
- final element = Element.empty('img');
- element.attributes['src'] = escapeHtml(destination);
- element.attributes['alt'] = state.textContent;
- if (title != null && title.isNotEmpty) {
- element.attributes['title'] = escapeAttribute(title);
- }
- return element;
- }
-
- // Add an image node to [parser]'s AST.
- //
- // If [label] is present, the potential image is treated as a reference image.
- // Otherwise, it is treated as an inline image.
- //
- // Returns whether the image was added successfully.
- @override
- bool _tryAddReferenceLink(InlineParser parser, TagState state, String label) {
- final element =
- _resolveReferenceLink(label, state, parser.document.linkReferences);
- if (element == null) {
- return false;
- }
- parser
- ..addNode(element)
- ..start = parser.pos;
- return true;
- }
-}
-
-/// Matches backtick-enclosed inline code blocks.
-class CodeSyntax extends InlineSyntax {
- CodeSyntax() : super(_pattern);
-
- // This pattern matches:
- //
- // * a string of backticks (not followed by any more), followed by
- // * a non-greedy string of anything, including newlines, ending with anything
- // except a backtick, followed by
- // * a string of backticks the same length as the first, not followed by any
- // more.
- //
- // This conforms to the delimiters of inline code, both in Markdown.pl, and
- // CommonMark.
- static const String _pattern = r'(`+(?!`))((?:.|\n)*?[^`])\1(?!`)';
-
- @override
- bool tryMatch(InlineParser parser, [int? startMatchPos]) {
- if (parser.pos > 0 && parser.charAt(parser.pos - 1) == $backquote) {
- // Not really a match! We can't just sneak past one backtick to try the
- // next character. An example of this situation would be:
- //
- // before ``` and `` after.
- // ^--parser.pos
- return false;
- }
-
- final match = pattern.matchAsPrefix(parser.source, parser.pos);
- if (match == null) {
- return false;
- }
- parser.writeText();
- if (onMatch(parser, match)) {
- parser.consume(match[0]!.length);
- }
- return true;
- }
-
- @override
- bool onMatch(InlineParser parser, Match match) {
- parser.addNode(Element.text('code', escapeHtml(match[2]!.trim())));
- return true;
- }
-}
-
-/// Matches GitHub Markdown emoji syntax like `:smile:`.
-///
-/// There is no formal specification of GitHub's support for this colon-based
-/// emoji support, so this syntax is based on the results of Markdown-enabled
-/// text fields at github.com.
-class EmojiSyntax extends InlineSyntax {
- // Emoji "aliases" are mostly limited to lower-case letters, numbers, and
- // underscores, but GitHub also supports `:+1:` and `:-1:`.
- EmojiSyntax() : super(':([a-z0-9_+-]+):');
-
- @override
- bool onMatch(InlineParser parser, Match match) {
- final alias = match[1];
- final emoji = emojis[alias!];
- if (emoji == null) {
- parser.advanceBy(1);
- return false;
- }
- parser.addNode(Text(emoji));
-
- return true;
- }
-}
-
-/// Keeps track of a currently open tag while it is being parsed.
-///
-/// The parser maintains a stack of these so it can handle nested tags.
-class TagState {
- TagState(this.startPos, this.endPos, this.syntax, this.openingDelimiterRun)
- : children = [];
-
- /// The point in the original source where this tag started.
- final int startPos;
-
- /// The point in the original source where open tag ended.
- final int endPos;
-
- /// The syntax that created this node.
- final TagSyntax? syntax;
-
- /// The children of this node. Will be `null` for text nodes.
- final List children;
-
- final DelimiterRun? openingDelimiterRun;
-
- /// Attempts to close this tag by matching the current text against its end
- /// pattern.
- bool tryMatch(InlineParser parser) {
- final endMatch =
- syntax!.endPattern.matchAsPrefix(parser.source, parser.pos);
- if (endMatch == null) {
- return false;
- }
-
- if (!syntax!.requiresDelimiterRun) {
- // Close the tag.
- close(parser, endMatch);
- return true;
- }
-
- // TODO: Move this logic into TagSyntax.
- final runLength = endMatch.group(0)!.length;
- final openingRunLength = endPos - startPos;
- final closingMatchStart = parser.pos;
- final closingMatchEnd = parser.pos + runLength - 1;
- final closingDelimiterRun =
- DelimiterRun.tryParse(parser, closingMatchStart, closingMatchEnd);
- if (closingDelimiterRun != null && closingDelimiterRun.canClose) {
- // Emphasis rules #9 and #10:
- final oneRunOpensAndCloses =
- (openingDelimiterRun!.canOpen && openingDelimiterRun!.canClose) ||
- (closingDelimiterRun.canOpen && closingDelimiterRun.canClose);
- if (oneRunOpensAndCloses &&
- (openingRunLength + closingDelimiterRun.length!) % 3 == 0) {
- return false;
- }
- // Close the tag.
- close(parser, endMatch);
- return true;
- } else {
- return false;
- }
- }
-
- /// Pops this tag off the stack, completes it, and adds it to the output.
- ///
- /// Will discard any unmatched tags that happen to be above it on the stack.
- /// If this is the last node in the stack, returns its children.
- List? close(InlineParser parser, Match? endMatch) {
- // If there are unclosed tags on top of this one when it's closed, that
- // means they are mismatched. Mismatched tags are treated as plain text in
- // markdown. So for each tag above this one, we write its start tag as text
- // and then adds its children to this one's children.
- final index = parser._stack.indexOf(this);
-
- // Remove the unmatched children.
- final unmatchedTags = parser._stack.sublist(index + 1);
- parser._stack.removeRange(index + 1, parser._stack.length);
-
- // Flatten them out onto this tag.
- for (final unmatched in unmatchedTags) {
- // Write the start tag as text.
- parser.writeTextRange(unmatched.startPos, unmatched.endPos);
-
- // Bequeath its children unto this tag.
- children.addAll(unmatched.children);
- }
-
- // Pop this off the stack.
- parser.writeText();
- parser._stack.removeLast();
-
- // If the stack is empty now, this is the special "results" node.
- if (parser._stack.isEmpty) {
- return children;
- }
- final endMatchIndex = parser.pos;
-
- // We are still parsing, so add this to its parent's children.
- if (syntax!.onMatchEnd(parser, endMatch!, this)) {
- parser.consume(endMatch[0]!.length);
- } else {
- // Didn't close correctly so revert to text.
- parser
- ..writeTextRange(startPos, endPos)
- .._stack.last.children.addAll(children)
- ..pos = endMatchIndex
- ..advanceBy(endMatch[0]!.length);
- }
-
- return null;
- }
-
- String get textContent => children.map((child) => child.textContent).join();
-}
-
-class InlineLink {
- InlineLink(this.destination, {this.title});
-
- final String destination;
- final String? title;
-}
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/util.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/util.dart
deleted file mode 100644
index aed4c3c3..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/util.dart
+++ /dev/null
@@ -1,71 +0,0 @@
-import 'dart:convert';
-
-import 'package:charcode/charcode.dart';
-
-String escapeHtml(String html) =>
- const HtmlEscape(HtmlEscapeMode.element).convert(html);
-
-// Escape the contents of [value], so that it may be used as an HTML attribute.
-
-// Based on http://spec.commonmark.org/0.28/#backslash-escapes.
-String escapeAttribute(String value) {
- final result = StringBuffer();
- int ch;
- for (var i = 0; i < value.codeUnits.length; i++) {
- ch = value.codeUnitAt(i);
- if (ch == $backslash) {
- i++;
- if (i == value.codeUnits.length) {
- result.writeCharCode(ch);
- break;
- }
- ch = value.codeUnitAt(i);
- switch (ch) {
- case $quote:
- result.write('"');
- break;
- case $exclamation:
- case $hash:
- case $dollar:
- case $percent:
- case $ampersand:
- case $apostrophe:
- case $lparen:
- case $rparen:
- case $asterisk:
- case $plus:
- case $comma:
- case $dash:
- case $dot:
- case $slash:
- case $colon:
- case $semicolon:
- case $lt:
- case $equal:
- case $gt:
- case $question:
- case $at:
- case $lbracket:
- case $backslash:
- case $rbracket:
- case $caret:
- case $underscore:
- case $backquote:
- case $lbrace:
- case $bar:
- case $rbrace:
- case $tilde:
- result.writeCharCode(ch);
- break;
- default:
- result.write('%5C');
- result.writeCharCode(ch);
- }
- } else if (ch == $quote) {
- result.write('%22');
- } else {
- result.writeCharCode(ch);
- }
- }
- return result.toString();
-}
diff --git a/packages/quill_html_converter/lib/src/packages/delta_markdown/version.dart b/packages/quill_html_converter/lib/src/packages/delta_markdown/version.dart
deleted file mode 100644
index 19433ffa..00000000
--- a/packages/quill_html_converter/lib/src/packages/delta_markdown/version.dart
+++ /dev/null
@@ -1,2 +0,0 @@
-// Generated code. Do not modify.
-const packageVersion = '0.0.2';
diff --git a/packages/quill_html_converter/pubspec.yaml b/packages/quill_html_converter/pubspec.yaml
index 236ba3d3..c75c0ed1 100644
--- a/packages/quill_html_converter/pubspec.yaml
+++ b/packages/quill_html_converter/pubspec.yaml
@@ -24,6 +24,7 @@ dependencies:
# markdown: ^7.1.1
charcode: ^1.3.1
collection: ^1.17.2
+ delta_markdown_converter: ^0.0.2
dev_dependencies:
flutter_test:
|