| // Copyright (c) 2022, 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 '../charcode.dart'; |
| import '../patterns.dart'; |
| import '../util.dart'; |
| import 'block_syntax.dart'; |
| import 'code_block_syntax.dart'; |
| import 'paragraph_syntax.dart'; |
| |
| /// Parses email-style blockquotes: `> quote`. |
| class BlockquoteSyntax extends BlockSyntax { |
| @override |
| RegExp get pattern => blockquotePattern; |
| |
| const BlockquoteSyntax(); |
| |
| @override |
| List<String> parseChildLines(BlockParser parser) { |
| // Grab all of the lines that form the blockquote, stripping off the ">". |
| final childLines = <String>[]; |
| |
| while (!parser.isDone) { |
| final currentLine = parser.current; |
| final match = pattern.firstMatch(parser.current); |
| if (match != null) { |
| // A block quote marker consists of a `>` together with an optional |
| // following space of indentation, see |
| // https://spec.commonmark.org/0.30/#block-quote-marker. |
| final markerStart = match.match.indexOf('>'); |
| int markerEnd; |
| if (currentLine.length > 1) { |
| final nextChar = currentLine.codeUnitAt(markerStart + 1); |
| final hasSpace = nextChar == $tab || nextChar == $space; |
| markerEnd = markerStart + (hasSpace ? 2 : 1); |
| } else { |
| markerEnd = markerStart + 1; |
| } |
| childLines.add(currentLine.substring(markerEnd)); |
| parser.advance(); |
| continue; |
| } |
| |
| final lastLine = childLines.last; |
| |
| // A paragraph continuation is OK. This is content that cannot be parsed |
| // as any other syntax except Paragraph, and it doesn't match the bar in |
| // a Setext header. |
| // Because indented code blocks cannot interrupt paragraphs, a line |
| // matched CodeBlockSyntax is also paragraph continuation text. |
| final otherMatched = |
| parser.blockSyntaxes.firstWhere((s) => s.canParse(parser)); |
| if ((otherMatched is ParagraphSyntax && |
| lastLine.isNotEmpty && |
| !codeFencePattern.hasMatch(lastLine)) || |
| (otherMatched is CodeBlockSyntax && |
| !indentPattern.hasMatch(lastLine))) { |
| childLines.add(parser.current); |
| parser.advance(); |
| } else { |
| break; |
| } |
| } |
| |
| return childLines; |
| } |
| |
| @override |
| Node parse(BlockParser parser) { |
| final childLines = parseChildLines(parser); |
| |
| // Recursively parse the contents of the blockquote. |
| final children = BlockParser(childLines, parser.document).parseLines(); |
| |
| return Element('blockquote', children); |
| } |
| } |