blob: a25da4b3fc233a829603a039dc620267b28f1df6 [file] [log] [blame]
// 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 '../patterns.dart';
import 'block_syntax.dart';
/// Parse HTML blocks.
// There are seven kinds of HTML block defined in the CommonMark spec:
// https://spec.commonmark.org/0.30/#html-blocks.
// These matching conditions and HTML block types mentioned in this syntax
// correspond to these ones in the CommonMark spec.
class HtmlBlockSyntax extends BlockSyntax {
@override
RegExp get pattern => htmlBlockPattern;
// All types of HTML blocks except type 7 may interrupt a paragraph, see the
// second paragraph after https://spec.commonmark.org/0.30/#example-148 for
// more detail.
@override
bool canEndBlock(BlockParser parser) =>
pattern.firstMatch(parser.current)!.namedGroup('condition_7') == null;
static final _endConditions = [
// For condition 1, it does not need to match the start tag, see
// https://spec.commonmark.org/0.30/#end-condition
RegExp('</(?:pre|script|style|textarea)>', caseSensitive: false),
RegExp('-->'),
RegExp(r'\?>'),
RegExp('>'),
RegExp(']]>'),
emptyPattern,
emptyPattern,
];
const HtmlBlockSyntax();
@override
List<String> parseChildLines(BlockParser parser) {
final lines = <String>[];
final match = pattern.firstMatch(parser.current);
var matchedCondition = 0;
for (var i = 0; i < match!.groupCount; i++) {
if (match.group(i + 1) != null) {
matchedCondition = i;
break;
}
}
final endCondition = _endConditions[matchedCondition];
if (endCondition == emptyPattern) {
lines.add(parser.current);
parser.advance();
while (!parser.isDone && !endCondition.hasMatch(parser.current)) {
lines.add(parser.current);
parser.advance();
}
} else {
while (!parser.isDone) {
lines.add(parser.current);
if (endCondition.hasMatch(parser.current)) {
break;
}
parser.advance();
}
parser.advance();
}
// If the following lines start an HTML block again, put them together with
// current HTML block.
if (!parser.isDone &&
parser.next != null &&
pattern.hasMatch(parser.next!)) {
parser.advance();
lines.addAll(parseChildLines(parser));
}
return lines;
}
@override
Node parse(BlockParser parser) {
final childLines = parseChildLines(parser);
return Text(childLines.join('\n').trimRight());
}
}