search method
search source path, text files, with regexp or replace
regexp: regex pattern.replace: regexp has match to replace if replace.isNoEmpty.extMime: addon text file type, use mimetype, e.g. {'tml':'text/toml', 'yml':'application/yaml'}.lineByLine: use line by line file processing?
final pattern = r'**.yaml';
final excludes = [r'.**'];
final source = '.';
final action = BasicPathAction(source, pattern:pattern, excludes: excludes);
final regexp = r'version: 1.0.\d+';
action.search(regexp);
Implementation
///
/// ```dart
/// final pattern = r'**.yaml';
/// final excludes = [r'.**'];
/// final source = '.';
/// final action = BasicPathAction(source, pattern:pattern, excludes: excludes);
///
/// final regexp = r'version: 1.0.\d+';
/// action.search(regexp);
/// ```
Stream<Es> search(
String regexp, {
String replace = '',
Map<String, String> extMime = const {},
bool reI = false, // Case-insensitive
bool reU = false,
bool reS = false,
bool reM = false,
bool lineByLine = true, // is LineSplitter
}) {
final action = PathAction.search.name, chk = 'validator';
argErr ??= validator();
if (argErr!.isNotEmpty) throw ArgumentError.value(argErr, action, chk);
final fields = fieldsFromOptions(
[FormatField.extra.toString(), ...fmtFields],
);
final fseType = FileSystemEntityType.file;
final stream = scEntity.stream;
final fStream =
stream.where((event) => event.fs.type == fseType).asBroadcastStream();
doTextMimeInit(); // use for isTextMimeType
if (extMime.isNotEmpty) extMime.forEach((k, v) => doTextMimeAdd(k, v));
// logger.stdout('d, regexp:$regexp, utf8.encode:${utf8.encode(regexp)}');
final regex = RegExp(
regexp,
caseSensitive: !reI,
unicode: reU,
dotAll: reS,
multiLine: reM,
);
late StreamSubscription subs;
subs = fStream.listen(
(event) {
var (entity, stat, extra) = event.asRecord;
final ok = stat.type == FileSystemEntityType.notFound ? false : true;
final isText = ok ? isTextMimeType(entity.path) : false;
extra += ' isTextFile:${isText ? 1 : 0}';
final line =
Formatter(entity, stat, extra, action, shows: fields, ok: isText);
logger.stdout(line.toString());
if (ok && isText) {
final buf = StringBuffer();
var replaced = false;
var no = 0; // lineNumber
final location = compressTilde(entity.path);
final file = File.fromUri(entity.uri);
var tStream = file.openRead().transform(utf8.decoder);
if (lineByLine) tStream = tStream.transform(const LineSplitter());
late StreamSubscription tSubs;
tSubs = tStream.listen(
(line) {
no++;
if (regex.hasMatch(line)) {
// line.contains(regex)
if (replace.isNotEmpty) {
replaced = true;
line = line.replaceAll(regex, replace);
logger.stdout('i, L:$no, O:$line, F:$location');
} else {
logger.stdout(
'i, L:$no, I:${lineByLine ? line : '......'}, F:$location');
}
}
if (replace.isNotEmpty) buf.writeln(line);
},
cancelOnError: cancelOnError,
onDone: () {
if (replace.isNotEmpty && replaced) {
try {
file.writeAsStringSync(buf.toString());
} on FileSystemException catch (e, s) {
logger
..trace('d, $action, file replace:$location')
..stderr('e, $action, error. $e')
..stderr(kIsDebug ? '$s' : '');
} finally {
buf.clear();
}
}
buf.clear();
// logger.trace('d, $action, done.');
},
onError: (e, s) {
buf.clear();
tSubs.cancel();
logger
..trace('d, $action, file listen:$location')
..stderr('e, $action, error. $e')
..stderr(kIsDebug ? '$s' : '');
},
);
}
},
cancelOnError: cancelOnError,
onDone: () => logger.trace('d, $action, done.'),
onError: (e, s) {
if (cancelOnError) {
exitCode = ExitCodeExt.error.code;
subs.cancel();
}
logger
..trace('d, $action, cancelOnError:$cancelOnError')
..stderr('e, $action, error. $e')
..stderr(kIsDebug ? '$s' : '');
},
);
return fStream;
}