search method
Future<SearchResult>
search(
- String pattern,
- SearchScope scope,
- SearchOptions options, {
- String? targetPath,
Search for pattern within the given scope.
Implementation
Future<SearchResult> search(
String pattern,
SearchScope scope,
SearchOptions options, {
String? targetPath,
}) async {
final sw = Stopwatch()..start();
// Build the regex for matching.
final regex = _buildRegex(pattern, options);
// Determine which files to search.
final files = await _resolveScope(scope, targetPath, options);
final matches = <SearchMatch>[];
var totalMatches = 0;
final maxResults = options.maxResults;
for (final filePath in files) {
if (maxResults != null && totalMatches >= maxResults) break;
final file = File(filePath);
if (!await file.exists()) continue;
String content;
try {
content = await file.readAsString();
} catch (_) {
continue; // skip binary / unreadable files
}
final lines = content.split('\n');
for (var i = 0; i < lines.length; i++) {
final lineMatches = regex.allMatches(lines[i]);
for (final m in lineMatches) {
totalMatches++;
if (maxResults != null && totalMatches > maxResults) break;
final beforeCtx = <String>[];
final afterCtx = <String>[];
for (var b = math.max(0, i - options.contextLines); b < i; b++) {
beforeCtx.add(lines[b]);
}
for (
var a = i + 1;
a <= math.min(lines.length - 1, i + options.contextLines);
a++
) {
afterCtx.add(lines[a]);
}
matches.add(
SearchMatch(
filePath: filePath,
lineNumber: i + 1,
column: m.start,
matchLength: m.end - m.start,
lineContent: lines[i],
beforeContext: beforeCtx,
afterContext: afterCtx,
),
);
}
}
}
sw.stop();
final truncated = maxResults != null && totalMatches > maxResults;
_addToHistory(pattern, scope, options);
return SearchResult(
matches: matches,
totalMatches: totalMatches,
filesSearched: files.length,
duration: sw.elapsed,
truncated: truncated,
);
}