chore: adopt Dart 3.9 toolchain — honest floor + tall-style reformat (T-353)
Raise the declared minimums in pubspec.yaml to what our deps already require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist 0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is the binding floor. Pin the exact build toolchain in .fvmrc (Flutter 3.44.1). Moving to the Dart 3.9 language level switches `dart format` to the new "tall" style and enables two new lints. This commit is the resulting mechanical churn, isolated from any behaviour change: - whole-tree `dart format` reformat (tall style) - `dart fix` for unnecessary_underscores + use_null_aware_elements No runtime behaviour change; `make test` green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,9 +69,7 @@ Stream<List<SearchMatch>> grepWorkspace({
|
||||
final rootPath = root.absolute.path;
|
||||
|
||||
// Launch every chunk concurrently; await in chunk order to stream.
|
||||
final futures = <Future<List<SearchMatch>>>[
|
||||
for (final c in chunks) _runChunk(rootPath, c, query, maxPerFile, useIsolates),
|
||||
];
|
||||
final futures = <Future<List<SearchMatch>>>[for (final c in chunks) _runChunk(rootPath, c, query, maxPerFile, useIsolates)];
|
||||
|
||||
var emitted = 0;
|
||||
for (final f in futures) {
|
||||
@@ -104,13 +102,7 @@ void _drain(List<Future<List<SearchMatch>>> futures) {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SearchMatch>> _runChunk(
|
||||
String rootPath,
|
||||
List<String> paths,
|
||||
SearchQuery query,
|
||||
int maxPerFile,
|
||||
bool useIsolates,
|
||||
) {
|
||||
Future<List<SearchMatch>> _runChunk(String rootPath, List<String> paths, SearchQuery query, int maxPerFile, bool useIsolates) {
|
||||
if (useIsolates) {
|
||||
return Isolate.run(() => grepChunk(rootPath, paths, query, maxPerFile));
|
||||
}
|
||||
@@ -133,12 +125,7 @@ List<List<String>> _chunk(List<String> items, int buckets) {
|
||||
/// Grep a chunk of files. Runs in a worker isolate (or in-process for
|
||||
/// tests). Reads each file, skips binaries, and collects up to
|
||||
/// [maxPerFile] matches per file.
|
||||
List<SearchMatch> grepChunk(
|
||||
String rootPath,
|
||||
List<String> relPaths,
|
||||
SearchQuery query,
|
||||
int maxPerFile,
|
||||
) {
|
||||
List<SearchMatch> grepChunk(String rootPath, List<String> relPaths, SearchQuery query, int maxPerFile) {
|
||||
final compiled = CompiledQuery(query);
|
||||
final out = <SearchMatch>[];
|
||||
for (final rel in relPaths) {
|
||||
@@ -160,25 +147,13 @@ List<SearchMatch> grepChunk(
|
||||
|
||||
/// Match [content]'s lines, appending up to [maxPerFile] hits to [out].
|
||||
/// Exposed (with a pre-built [compiled]) for unit testing without I/O.
|
||||
void grepContent(
|
||||
String relPath,
|
||||
String content,
|
||||
CompiledQuery compiled,
|
||||
int maxPerFile,
|
||||
List<SearchMatch> out,
|
||||
) {
|
||||
void grepContent(String relPath, String content, CompiledQuery compiled, int maxPerFile, List<SearchMatch> out) {
|
||||
var lineNo = 0;
|
||||
final added0 = out.length;
|
||||
for (final line in const LineSplitter().convert(content)) {
|
||||
lineNo++;
|
||||
for (final span in compiled.matches(line)) {
|
||||
out.add(SearchMatch(
|
||||
path: relPath,
|
||||
line: lineNo,
|
||||
matchStart: span.$1,
|
||||
matchEnd: span.$2,
|
||||
preview: line.length > 500 ? line.substring(0, 500) : line,
|
||||
));
|
||||
out.add(SearchMatch(path: relPath, line: lineNo, matchStart: span.$1, matchEnd: span.$2, preview: line.length > 500 ? line.substring(0, 500) : line));
|
||||
if (out.length - added0 >= maxPerFile) return;
|
||||
}
|
||||
}
|
||||
@@ -188,9 +163,9 @@ void grepContent(
|
||||
/// matcher with an optional case-insensitive fast-path.
|
||||
class CompiledQuery {
|
||||
CompiledQuery(SearchQuery q)
|
||||
: _regex = q.regex ? RegExp(q.pattern, caseSensitive: !q.ignoreCase) : null,
|
||||
_needle = q.regex ? '' : (q.ignoreCase ? q.pattern.toLowerCase() : q.pattern),
|
||||
_ignoreCase = q.ignoreCase;
|
||||
: _regex = q.regex ? RegExp(q.pattern, caseSensitive: !q.ignoreCase) : null,
|
||||
_needle = q.regex ? '' : (q.ignoreCase ? q.pattern.toLowerCase() : q.pattern),
|
||||
_ignoreCase = q.ignoreCase;
|
||||
|
||||
final RegExp? _regex;
|
||||
final String _needle;
|
||||
|
||||
+16
-40
@@ -9,13 +9,7 @@ library;
|
||||
/// result, navigate to the line, and (for replace, T-53) address the
|
||||
/// matched span within the line.
|
||||
class SearchMatch {
|
||||
const SearchMatch({
|
||||
required this.path,
|
||||
required this.line,
|
||||
required this.matchStart,
|
||||
required this.matchEnd,
|
||||
required this.preview,
|
||||
});
|
||||
const SearchMatch({required this.path, required this.line, required this.matchStart, required this.matchEnd, required this.preview});
|
||||
|
||||
/// Repo-relative, forward-slashed path of the file.
|
||||
final String path;
|
||||
@@ -32,32 +26,20 @@ class SearchMatch {
|
||||
/// The matched line's text (capped for transport/render).
|
||||
final String preview;
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'path': path,
|
||||
'line': line,
|
||||
'matchStart': matchStart,
|
||||
'matchEnd': matchEnd,
|
||||
'preview': preview,
|
||||
};
|
||||
Map<String, Object?> toJson() => {'path': path, 'line': line, 'matchStart': matchStart, 'matchEnd': matchEnd, 'preview': preview};
|
||||
|
||||
factory SearchMatch.fromJson(Map<String, Object?> j) => SearchMatch(
|
||||
path: j['path'] as String,
|
||||
line: (j['line'] as num).toInt(),
|
||||
matchStart: (j['matchStart'] as num).toInt(),
|
||||
matchEnd: (j['matchEnd'] as num).toInt(),
|
||||
preview: j['preview'] as String,
|
||||
);
|
||||
path: j['path'] as String,
|
||||
line: (j['line'] as num).toInt(),
|
||||
matchStart: (j['matchStart'] as num).toInt(),
|
||||
matchEnd: (j['matchEnd'] as num).toInt(),
|
||||
preview: j['preview'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters for a workspace search.
|
||||
class SearchQuery {
|
||||
const SearchQuery({
|
||||
required this.pattern,
|
||||
this.regex = false,
|
||||
this.ignoreCase = false,
|
||||
this.include = const [],
|
||||
this.exclude = const [],
|
||||
});
|
||||
const SearchQuery({required this.pattern, this.regex = false, this.ignoreCase = false, this.include = const [], this.exclude = const []});
|
||||
|
||||
/// The literal text (when [regex] is false) or regular expression
|
||||
/// source (when true) to search for.
|
||||
@@ -71,21 +53,15 @@ class SearchQuery {
|
||||
/// Glob patterns; matching paths are skipped (applied after [include]).
|
||||
final List<String> exclude;
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'pattern': pattern,
|
||||
'regex': regex,
|
||||
'ignoreCase': ignoreCase,
|
||||
'include': include,
|
||||
'exclude': exclude,
|
||||
};
|
||||
Map<String, Object?> toJson() => {'pattern': pattern, 'regex': regex, 'ignoreCase': ignoreCase, 'include': include, 'exclude': exclude};
|
||||
|
||||
factory SearchQuery.fromJson(Map<String, Object?> j) => SearchQuery(
|
||||
pattern: (j['pattern'] as String?) ?? '',
|
||||
regex: j['regex'] == true,
|
||||
ignoreCase: j['ignoreCase'] == true,
|
||||
include: _strings(j['include']),
|
||||
exclude: _strings(j['exclude']),
|
||||
);
|
||||
pattern: (j['pattern'] as String?) ?? '',
|
||||
regex: j['regex'] == true,
|
||||
ignoreCase: j['ignoreCase'] == true,
|
||||
include: _strings(j['include']),
|
||||
exclude: _strings(j['exclude']),
|
||||
);
|
||||
|
||||
static List<String> _strings(Object? v) => v is List ? [for (final e in v) '$e'] : const [];
|
||||
}
|
||||
|
||||
@@ -28,11 +28,8 @@ class ReplacementEdit {
|
||||
|
||||
Map<String, Object?> toJson() => {'line': line, 'before': before, 'after': after};
|
||||
|
||||
factory ReplacementEdit.fromJson(Map<String, Object?> j) => ReplacementEdit(
|
||||
line: (j['line'] as num).toInt(),
|
||||
before: j['before'] as String,
|
||||
after: j['after'] as String,
|
||||
);
|
||||
factory ReplacementEdit.fromJson(Map<String, Object?> j) =>
|
||||
ReplacementEdit(line: (j['line'] as num).toInt(), before: j['before'] as String, after: j['after'] as String);
|
||||
}
|
||||
|
||||
/// The set of edits a replacement would make to one file.
|
||||
@@ -46,18 +43,16 @@ class FileReplacement {
|
||||
final List<ReplacementEdit> edits;
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'path': path,
|
||||
'count': count,
|
||||
'edits': [for (final e in edits) e.toJson()],
|
||||
};
|
||||
'path': path,
|
||||
'count': count,
|
||||
'edits': [for (final e in edits) e.toJson()],
|
||||
};
|
||||
|
||||
factory FileReplacement.fromJson(Map<String, Object?> j) => FileReplacement(
|
||||
path: j['path'] as String,
|
||||
count: (j['count'] as num).toInt(),
|
||||
edits: [
|
||||
for (final e in (j['edits'] as List? ?? const []).whereType<Map>()) ReplacementEdit.fromJson(e.cast<String, Object?>()),
|
||||
],
|
||||
);
|
||||
path: j['path'] as String,
|
||||
count: (j['count'] as num).toInt(),
|
||||
edits: [for (final e in (j['edits'] as List? ?? const []).whereType<Map>()) ReplacementEdit.fromJson(e.cast<String, Object?>())],
|
||||
);
|
||||
}
|
||||
|
||||
/// Apply [query]'s pattern to [text], substituting [replacement]. Returns
|
||||
|
||||
Reference in New Issue
Block a user