markdownToPlainText function
Converts Markdown source to a readable plain-text representation.
This mirrors Scratch's copy-as-plain-text behavior: Markdown structure is stripped outside fenced code blocks, while code block content is preserved.
Implementation
String markdownToPlainText(String markdown) {
var inCodeBlock = false;
final plainLines = <String>[];
for (final line in markdown.split(RegExp(r'\r?\n'))) {
var text = line;
if (RegExp(r'^\s*(```|~~~)').hasMatch(text)) {
inCodeBlock = !inCodeBlock;
plainLines.add('');
continue;
}
if (!inCodeBlock) {
text = text
.replaceFirst(RegExp(r'^\s{0,3}#{1,6}\s+'), '')
.replaceFirst(RegExp(r'^\s{0,3}>\s?'), '')
.replaceFirst(RegExp(r'^\s*([-*+]\s+|\d+\.\s+)'), '')
.replaceFirst(RegExp(r'^\[[ xX]\]\s+'), '')
.replaceFirst(RegExp(r'^\s*([*-]){3,}\s*$'), '');
text = _replaceAllMapped(text, RegExp(r'!\[(.*?)\]\([^)]*\)'), 1);
text = _replaceAllMapped(text, RegExp(r'\[(.+?)\]\([^)]*\)'), 1);
text = _replaceAllMapped(text, RegExp(r'`([^`]+)`'), 1);
text = _replaceAllMapped(text, RegExp(r'\*\*(.+?)\*\*'), 1);
text = _replaceUnderscoreDelimiterPairs(
text,
'__',
(content) => content,
);
text = _replaceAllMapped(text, RegExp(r'\*(.+?)\*'), 1);
text = _replaceUnderscoreDelimiterPairs(
text,
'_',
(content) => content,
);
text = _replaceAllMapped(text, RegExp(r'~~(.+?)~~'), 1);
}
plainLines.add(text);
}
return plainLines
.join('\n')
.replaceAll(RegExp(r'\n{3,}'), '\n\n')
.trimRight();
}