// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:io'; /// Calculates the amount of formatting changes in a given directory of code. /// /// This should be run with a path to a directory. That directory should /// contain a Git repo. The committed state of the repo should be the formatted /// output before the change in question. Then the result of the new formatting /// should be unstaged changes. /// /// Uses `git diff --shortstat` to calculate the number of changed lines. /// /// Counts the number of lines of Dart code by reading the files. void main(List arguments) { if (arguments.length != 1) { print('Usage: change_metrics.dart '); exit(1); } var directory = arguments[0]; var totalLines = 0; var totalFiles = 0; print('Counting lines...'); for (var entry in Directory(directory).listSync(recursive: true)) { if (entry is File && entry.path.endsWith('.dart')) { try { var lines = entry.readAsLinesSync(); totalFiles++; totalLines += lines.length; } catch (error) { print('Could not read ${entry.path}:\n$error'); } } } print('Getting diff stats...'); var result = Process.runSync( 'git', ['diff', '--shortstat'], // Make sure the user's local Git config doesn't affect the output. environment: {'GIT_CONFIG_NOGLOBAL': 'true', 'GIT_CONFIG_NOSYSTEM': 'true'}, workingDirectory: directory, ); if (result.exitCode != 0) { print('Git failure:\n${result.stdout}\n${result.stderr}'); exit(1); } var stdout = result.stdout as String; var insertions = _parseGitStdout(stdout, r'(\d+) insertions'); var deletions = _parseGitStdout(stdout, r'(\d+) deletions'); var changes = insertions + deletions; print('$totalLines lines in $totalFiles files'); print('$insertions insertions + $deletions deletions = $changes changes'); var linesPerChange = totalLines / changes; print( '1 changed line for every ${linesPerChange.toStringAsFixed(2)} ' 'lines of code', ); var changesPerLine = 1000.0 * changes / totalLines; print( '${changesPerLine.toStringAsFixed(4)} ' 'changed lines for every 1,000 lines of code', ); } int _parseGitStdout(String stdout, String pattern) { var match = RegExp(pattern).firstMatch(stdout); if (match == null) { print('Could not parse Git output:\n$stdout'); exit(1); } return int.parse(match[1]!); }