findImportBlockIndices static method
Finds the indices of the import block (library, import, export directives) and the index of the first line after it. Handles multi-line show/hide clauses correctly.
Implementation
@visibleForTesting
static ({int? importStart, int? importEnd}) findImportBlockIndices(
List<String> lines,
) {
int? importStart;
int? importEnd;
int? commentStart;
final skippable = [
'library',
'as',
'hide',
'show',
'export',
RegExp(r'^\w+[,;]$'),
];
var inShowOrHide = false;
bool endsWithSemicolon(String line) {
final withoutComment = line.contains('//')
? line.substring(0, line.indexOf('//')).trimRight()
: line;
return withoutComment.endsWith(';');
}
bool skippableMatches(String trimmed) => skippable.any(
(s) => s is RegExp ? s.hasMatch(trimmed) : trimmed.startsWith(s),
);
for (final (index, line) in lines.indexed) {
final trimmed = line.trim();
if (trimmed.isEmpty) continue;
if (trimmed.startsWith('//')) {
if (trimmed != '// dart format on') {
commentStart ??= index;
}
continue;
}
// When inside a multi-line show/hide clause, continue until we find
// the terminating semicolon (ignoring any trailing comment).
if (inShowOrHide) {
if (endsWithSemicolon(trimmed)) {
inShowOrHide = false;
}
continue;
}
// A line starting with show/hide may span multiple lines until `;`.
if (trimmed.startsWith('show') || trimmed.startsWith('hide')) {
inShowOrHide = !endsWithSemicolon(trimmed);
continue;
}
if (skippableMatches(trimmed)) {
continue;
}
if (trimmed.startsWith('import ')) {
importStart ??= index;
commentStart = null;
continue;
}
importEnd = commentStart ?? index;
break;
}
return (importStart: importStart, importEnd: importEnd);
}