getInputs static method

FormatterInputs getInputs({
  1. List<Glob>? exclude,
  2. bool? expandCwd,
  3. bool? followLinks,
  4. String? root,
  5. bool? collapseDirectories,
})

Builds and returns the object that contains:

  • The file paths
  • The paths that were excluded by an exclude glob
  • The paths that were skipped because they are links
  • The hidden directories(start with a '.') that were skipped

The file paths will include all .dart files in path (and its subdirectories), except any paths that match the expanded exclude globs.

By default these globs are assumed to be relative to the current working directory, but that can be overridden via root for testing purposes.

If collapseDirectories is true, directories that contain no exclusions will wind up in the FormatterInputs, rather than each file in that tree. You may get unexpected results if this and followLinks are both true.

Implementation

static FormatterInputs getInputs({
  List<Glob>? exclude,
  bool? expandCwd,
  bool? followLinks,
  String? root,
  bool? collapseDirectories,
}) {
  expandCwd ??= false;
  followLinks ??= false;
  collapseDirectories ??= false;

  final includedFiles = <String>{};
  final excludedFiles = <String>{};
  final skippedLinks = <String>{};
  final hiddenDirectories = <String>{};

  exclude ??= <Glob>[];

  if (exclude.isEmpty && !expandCwd) {
    return FormatterInputs({'.'});
  }

  final dir = Directory(root ?? '.');

  // Use Glob.listSync to get all directories which might include a matching file.
  var directoriesWithExcludes = <String>{};

  if (collapseDirectories) {
    for (var g in exclude) {
      List<FileSystemEntity>? matchingPaths;
      try {
        matchingPaths = g.listSync(followLinks: followLinks);
      } on FileSystemException catch (_) {
        _log.finer("Glob '$g' did not match any paths.\n");
      }
      if (matchingPaths != null) {
        for (var path in matchingPaths) {
          if (path is Directory) {
            directoriesWithExcludes.add(path.path);
          } else {
            directoriesWithExcludes.add(path.parent.path);
          }
        }
      }
    }

    // This is all the directories that contain a match within them.
    _log.finer("Directories with excludes:\n");
    for (var dir in directoriesWithExcludes) {
      _log.finer("  $dir\n");
    }
    _log.finer(
        "${directoriesWithExcludes.length} directories contain excludes\n");
  }

  String currentDirectory = p.relative(dir.path, from: dir.path);
  bool skipFilesInDirectory = false;
  for (final entry
      in dir.listSync(recursive: true, followLinks: followLinks)) {
    final relative = p.relative(entry.path, from: dir.path);
    _log.finest('== Processing relative $relative ==\n');

    if (p.isWithin(currentDirectory, relative)) {
      if (skipFilesInDirectory) {
        _log.finest('skipping child $entry\n');
        continue;
      }
    } else {
      // the file/dir in not inside, cancel skipping.
      skipFilesInDirectory = false;
    }

    if (entry is Link) {
      _log.finer('skipping link $relative\n');
      skippedLinks.add(relative);
      continue;
    }

    if (entry is File && !entry.path.endsWith('.dart')) {
      _log.finest('skipping non-dart file $relative\n');
      continue;
    }

    // If the path is in a subdirectory starting with ".", ignore it.
    final parts = p.split(relative);
    int? hiddenIndex;
    for (var i = 0; i < parts.length; i++) {
      if (parts[i].startsWith(".")) {
        hiddenIndex = i;
        break;
      }
    }

    if (hiddenIndex != null) {
      final hiddenDirectory = p.joinAll(parts.take(hiddenIndex + 1));
      hiddenDirectories.add(hiddenDirectory);
      _log.finest('skipping file $relative in hidden dir $hiddenDirectory\n');
      if (collapseDirectories) {
        currentDirectory = hiddenDirectory;
        skipFilesInDirectory = true;
      }
      continue;
    }

    if (exclude.any((glob) => glob.matches(relative))) {
      _log.finer('excluding $relative\n');
      excludedFiles.add(relative);
    } else {
      if (collapseDirectories && entry is Directory) {
        _log.finest('directory: $entry\n');
        currentDirectory = relative;
        // It seems we can rely on the order of files coming from Directory.listSync.
        // If the entry does not contain an excluded file,
        // we skip adding any of its children files or directories.
        if (directoriesWithExcludes.any(
          (directoryWithExclude) =>
              p.isWithin(entry.path, directoryWithExclude) ||
              p.equals(entry.path, directoryWithExclude),
        )) {
          _log.finer('$relative has excludes\n');
        } else {
          skipFilesInDirectory = true;
          _log.finer("$relative does not have excludes, skipping children\n");
          includedFiles.add(relative);
        }
      }

      if (entry is File && !skipFilesInDirectory) {
        _log.finest("adding $relative\n");
        includedFiles.add(relative);
      }
    }
  }

  _log.finer("excluded ${excludedFiles.length} files\n");

  return FormatterInputs(includedFiles,
      excludedFiles: excludedFiles,
      skippedLinks: skippedLinks,
      hiddenDirectories: hiddenDirectories);
}