search method

Stream<Es> search(
  1. String regexp, {
  2. String replace = '',
  3. Map<String, String> extMime = const {},
  4. bool reI = false,
  5. bool reU = false,
  6. bool reS = false,
  7. bool reM = false,
  8. bool lineByLine = true,
})

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;
}