blob: 1d30667a5e0cfe509a5ec5d9bcda66cc57d3ca24 [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 '../line.dart';
import '../patterns.dart';
import '../util.dart';
import 'block_syntax.dart';
/// Parses preformatted code blocks that are indented four spaces.
class CodeBlockSyntax extends BlockSyntax {
@override
RegExp get pattern => indentPattern;
@override
bool canEndBlock(BlockParser parser) => false;
const CodeBlockSyntax();
@override
List<Line> parseChildLines(BlockParser parser) {
final childLines = <Line>[];
while (!parser.isDone) {
final isBlankLine = parser.current.isBlankLine;
if (isBlankLine && _shouldEnd(parser)) {
break;
}
if (!isBlankLine &&
childLines.isNotEmpty &&
!pattern.hasMatch(parser.current.content)) {
break;
}
childLines.add(Line(
parser.current.content.dedent().text,
tabRemaining: parser.current.tabRemaining,
));
parser.advance();
}
return childLines;
}
@override
Node parse(BlockParser parser) {
final childLines = parseChildLines(parser);
// The Markdown tests expect a trailing newline.
childLines.add(Line(''));
var content = childLines
.map((e) => e.content.prependSpace(e.tabRemaining ?? 0))
.join('\n');
if (parser.document.encodeHtml) {
content = escapeHtml(content, escapeApos: false);
}
return Element('pre', [Element.text('code', content)]);
}
bool _shouldEnd(BlockParser parser) {
var i = 1;
while (true) {
final nextLine = parser.peek(i);
// EOF
if (nextLine == null) {
return true;
}
// It does not matter how many blank lines between chunks:
// https://spec.commonmark.org/0.30/#example-111
if (nextLine.isBlankLine) {
i++;
continue;
}
return !pattern.hasMatch(nextLine.content);
}
}
}