Skip to content

Commit f1cc57e

Browse files
authored
Metrics script (#1216)
* Add a script for tracking the impact of formatting changes. I use this when evaluating a style change as part of the change process: https://github.com/dart-lang/dart_style/wiki/Change-process * Update version.
1 parent 999380c commit f1cc57e

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

tool/change_metrics.dart

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
import 'dart:io';
5+
6+
/// Calculates the amount of formatting changes in a given directory of code.
7+
///
8+
/// This should be run with a path to a directory. That directory should
9+
/// contain a Git repo. The committed state of the repo should be the formatted
10+
/// output before the change in question. Then the result of the new formatting
11+
/// should be unstaged changes.
12+
///
13+
/// Uses `git diff --shortstat` to calculate the number of changed lines.
14+
///
15+
/// Counts the number of lines of Dart code by reading the files.
16+
void main(List<String> arguments) {
17+
if (arguments.length != 1) {
18+
print('Usage: change_metrics.dart <dir>');
19+
exit(1);
20+
}
21+
22+
var directory = arguments[0];
23+
var totalLines = 0;
24+
var totalFiles = 0;
25+
26+
print('Counting lines...');
27+
for (var entry in Directory(directory).listSync(recursive: true)) {
28+
if (entry is File && entry.path.endsWith('.dart')) {
29+
try {
30+
var lines = entry.readAsLinesSync();
31+
totalFiles++;
32+
totalLines += lines.length;
33+
} catch (error) {
34+
print('Could not read ${entry.path}:\n$error');
35+
}
36+
}
37+
}
38+
39+
print('Getting diff stats...');
40+
var result = Process.runSync('git', ['diff', '--shortstat'],
41+
// Make sure the user's local Git config doesn't affect the output.
42+
environment: {
43+
'GIT_CONFIG_NOGLOBAL': 'true',
44+
'GIT_CONFIG_NOSYSTEM': 'true',
45+
},
46+
workingDirectory: directory);
47+
if (result.exitCode != 0) {
48+
print('Git failure:\n${result.stdout}\n${result.stderr}');
49+
exit(1);
50+
}
51+
52+
var stdout = result.stdout as String;
53+
var insertions = _parseGitStdout(stdout, r'(\d+) insertions');
54+
var deletions = _parseGitStdout(stdout, r'(\d+) deletions');
55+
var changes = insertions + deletions;
56+
57+
print('$totalLines lines in $totalFiles files');
58+
print('$insertions insertions + $deletions deletions = $changes changes');
59+
var linesPerChange = totalLines / changes;
60+
print('1 changed line for every ${linesPerChange.toStringAsFixed(2)} '
61+
'lines of code');
62+
63+
var changesPerLine = 1000.0 * changes / totalLines;
64+
print('${changesPerLine.toStringAsFixed(4)} '
65+
'changed lines for every 1,000 lines of code');
66+
}
67+
68+
int _parseGitStdout(String stdout, String pattern) {
69+
var match = RegExp(pattern).firstMatch(stdout);
70+
if (match == null) {
71+
print('Could not parse Git output:\n$stdout');
72+
exit(1);
73+
}
74+
75+
return int.parse(match[1]!);
76+
}

0 commit comments

Comments
 (0)