dartCodeWithoutComments function
source with its comments removed and everything else — string literals
included — kept, newlines and all.
stripDartComments reads a string as running to the next quote of its
kind, which is right for the prose scans it serves and wrong for a
structural one: '"${v.replaceAll('"', '""')}"' ends early there, and
every comment after it is read as code. This one follows an
interpolation's braces, so a quote inside ${…} does not end the string.
Implementation
String dartCodeWithoutComments(String source) {
final s = source;
final out = StringBuffer();
late final int Function(int, bool) code;
int string(int i) {
final raw = i > 0 && (s[i - 1] == 'r' || s[i - 1] == 'R');
final quote = s[i];
final terminator = s.startsWith(quote * 3, i) ? quote * 3 : quote;
out.write(terminator);
i += terminator.length;
while (i < s.length && !s.startsWith(terminator, i)) {
if (!raw && s[i] == r'\' && i + 1 < s.length) {
out.write(s.substring(i, i + 2));
i += 2;
continue;
}
if (!raw && s.startsWith(r'${', i)) {
out.write(r'${');
i = code(i + 2, true);
continue;
}
out.write(s[i]);
i++;
}
if (i < s.length) out.write(terminator);
return i + terminator.length;
}
code = (int i, bool untilBrace) {
var depth = 0;
while (i < s.length) {
final c = s[i];
if (s.startsWith('//', i)) {
while (i < s.length && s[i] != '\n') {
i++;
}
continue;
}
if (s.startsWith('/*', i)) {
var nesting = 0;
while (i < s.length) {
if (s.startsWith('/*', i)) {
nesting++;
i += 2;
} else if (s.startsWith('*/', i)) {
i += 2;
if (--nesting == 0) break;
} else {
if (s[i] == '\n') out.write('\n');
i++;
}
}
continue;
}
if (c == "'" || c == '"') {
i = string(i);
continue;
}
if (untilBrace) {
if (c == '{') depth++;
if (c == '}') {
if (depth == 0) {
out.write('}');
return i + 1;
}
depth--;
}
}
out.write(c);
i++;
}
return i;
};
code(0, false);
return out.toString();
}