apply include/exclude globs in search.replace (T-364)

computeReplacements accepted the query's glob filters and silently
dropped them — replace could rewrite files the equivalent search
would never have matched. The grep engine's glob helpers are now
public and shared, so search and replace can't disagree on scope;
both the preview and the apply path go through the filtered list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 00:34:47 +02:00
co-authored by Claude Fable 5
parent 8477e302ef
commit 88d72789f4
6 changed files with 51 additions and 5 deletions
+24
View File
@@ -104,6 +104,30 @@ void main() {
);
expect(r.any((f) => f.path == 'blob.bin'), isFalse);
});
// T-364: the globs were accepted and silently dropped — replace touched
// files the equivalent search would never have matched.
test('include glob restricts replacement to matching files', () async {
File('${root.path}/c.txt').writeAsStringSync('foo here too\n');
final r = await computeReplacements(
root: root,
ignore: IgnoreSet([]),
query: const SearchQuery(pattern: 'foo', include: ['*.dart']),
replacement: 'baz',
);
expect(r.map((f) => f.path).toList(), ['a.dart']);
});
test('exclude glob is honored', () async {
File('${root.path}/c.txt').writeAsStringSync('foo here too\n');
final r = await computeReplacements(
root: root,
ignore: IgnoreSet([]),
query: const SearchQuery(pattern: 'foo', exclude: ['*.dart']),
replacement: 'baz',
);
expect(r.map((f) => f.path).toList(), ['c.txt']);
});
});
group('rewriteFileContent', () {