Files
Paul Berry afcfbbeba8 Migrate developer experience packages to new constructor decl syntax.
(Part of https://github.com/dart-lang/sdk/issues/63288)

This change migrates the packages owned by the developer experience
team to use the new constructor declaration syntax, described in
https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations.

This change was performed in an automated fashion, by (a) bumping the
packages' SDK constraints to `3.13.0-0`, (b) enabling the lints
`unnecessary_type_name_in_constructor` and
`unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint
failures using `dart fix`, and then (d) reformatting the affected
files.

To ease code review, I've reverted unrelated formatting changes.

Since this change requires bumping SDK constaints to `3.13.0-0`, it
was only performed on packages that are *not* published on
pub. (Packages that *are* published on pub should remain on lower
language versions until at least after the stable version of 3.13 is
released, so that we don't block users on the stable channel from
receiving updates to those packages.)

Change-Id: Ibb4daebafd239da58251e838ea6a3f336a6a6964
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505046
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
2026-05-27 14:52:58 -07:00

465 lines
12 KiB
Dart

// Copyright (c) 2020, 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:math' as math;
import 'dart:typed_data';
import 'package:analysis_server/src/status/pages.dart';
import 'output_utilities.dart';
/// https://en.wikipedia.org/wiki/Average#Arithmetic_mean
class ArithmeticMeanComputer {
final String name;
int sum = 0;
int count = 0;
int? min;
int? max;
new(this.name);
double get mean => sum / count;
/// Add the data from the given [computer] to this computer.
void addData(ArithmeticMeanComputer computer) {
sum += computer.sum;
count += computer.count;
min = _min(min, computer.min);
max = _max(max, computer.max);
}
void addValue(int val) {
sum += val;
count++;
min = _min(min, val);
max = _max(max, val);
}
void clear() {
sum = 0;
count = 0;
}
/// Set the state of this computer to the state recorded in the decoded JSON
/// [map].
void fromJson(Map<String, dynamic> map) {
sum = map['sum'] as int;
count = map['count'] as int;
min = map['min'] as int?;
max = map['max'] as int?;
}
/// Return a map used to represent this computer in a JSON structure.
Map<String, dynamic> toJson() {
return {
'sum': sum,
'count': count,
if (min != null) 'min': min,
if (max != null) 'max': max,
};
}
int? _max(int? first, int? second) {
if (first == null) {
return second;
} else if (second == null) {
return first;
} else {
return math.max(first, second);
}
}
int? _min(int? first, int? second) {
if (first == null) {
return second;
} else if (second == null) {
return first;
} else {
return math.min(first, second);
}
}
}
/// A simple counter class. A [String] name is passed to name the counter. Each
/// time something is counted, a non-null, non-empty [String] key is passed to
/// [count] to increment the amount from zero. [printCounterValues] is provided
/// to have a [String] summary of the generated counts, example:
///
/// ```
/// Counts for 'counter example':
/// [bucket-1] 60 (60.0%)
/// [bucket-2] 25 (25.0%)
/// [bucket-3] 5 (5.0%)
/// [bucket-4] 10 (10.0%)
/// ```
class Counter {
final String name;
final Map<String, int> _buckets = {};
int _totalCount = 0;
new(this.name);
/// Return a copy of all the current count data, this getter copies and
/// returns the data to ensure that the data is only modified with the public
/// accessors in this class.
Map<String, int> get map => Map.from(_buckets);
int get totalCount => _totalCount;
/// Add the data from the given [counter] to this counter.
void addData(Counter counter) {
for (var entry in counter._buckets.entries) {
var bucket = entry.key;
_buckets[bucket] = (_buckets[bucket] ?? 0) + entry.value;
}
_totalCount += counter._totalCount;
}
void clear() {
_buckets.clear();
_totalCount = 0;
}
void count(String id, [int countNumber = 1]) {
assert(id.isNotEmpty && 1 <= countNumber);
_buckets.update(
id,
(value) => value + countNumber,
ifAbsent: () => countNumber,
);
_totalCount += countNumber;
}
/// Set the state of this counter to the state recorded in the decoded JSON
/// [map].
void fromJson(Map<String, dynamic> map) {
for (var entry in (map['buckets'] as Map<String, dynamic>).entries) {
_buckets[entry.key] = entry.value as int;
}
_totalCount = map['totalCount'] as int;
}
int getCountOf(String id) => _buckets[id] ?? 0;
void printCounterValues() {
if (_totalCount > 0) {
var table = [
['', 'count', 'percent'],
];
var entries = _buckets.entries.toList();
entries.sort((first, second) => second.value - first.value);
for (var entry in entries) {
var id = entry.key;
var count = entry.value;
table.add([
id,
count.toString(),
printPercentage(count / _totalCount, 2),
]);
}
printTable(table);
} else {
print('<no counts>');
}
}
/// Return a map used to represent this counter in a JSON structure.
Map<String, dynamic> toJson() {
return {'buckets': _buckets, 'totalCount': _totalCount};
}
}
class DistributionComputer {
/// The buckets in which values are counted: [0..9], [10..19], ... [100..].
List<int> buckets = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
/// Add the data from the given [computer] to this computer.
void addData(DistributionComputer computer) {
for (var i = 0; i < buckets.length; i++) {
buckets[i] += computer.buckets[i];
}
}
/// Add a millisecond value to the list of buckets.
void addValue(int value) {
var bucket = math.min(value ~/ 10, buckets.length - 1);
buckets[bucket]++;
}
/// Return a textual representation of the distribution.
String displayString() {
var buffer = StringBuffer();
for (var i = 0; i < buckets.length; i++) {
if (i > 0) {
buffer.write(' ');
}
buffer.write('[');
buffer.write(i * 10);
buffer.write('] ');
buffer.write(buckets[i]);
}
return buffer.toString();
}
/// Set the state of this computer to the state recorded in the decoded JSON
/// [map].
void fromJson(Map<String, dynamic> map) {
buckets = map['buckets'] as List<int>;
}
/// Return a map used to represent this computer in a JSON structure.
Map<String, dynamic> toJson() {
return {'buckets': buckets};
}
}
/// A computer for the mean reciprocal rank. The MRR as well as the MRR only
/// if the item was in the top 5 in the list see [_maxRank], is computed.
/// https://en.wikipedia.org/wiki/Mean_reciprocal_rank.
class MeanReciprocalRankComputer {
static const int _maxRank = 5;
final String name;
double _sum = 0;
double _sum_5 = 0;
int _count = 0;
new(this.name);
int get count => _count;
double get mrr {
if (count == 0) {
return 0;
}
return _sum / count;
}
double get mrr_5 {
if (count == 0) {
return 0;
}
return _sum_5 / count;
}
/// Add the data from the given [computer] to this computer.
void addData(MeanReciprocalRankComputer computer) {
_sum += computer._sum;
_sum_5 += computer._sum_5;
_count += computer._count;
}
void addRank(int rank) {
if (rank != 0) {
_sum += 1 / rank;
if (rank <= _maxRank) {
_sum_5 += 1 / rank;
}
}
_count++;
}
void clear() {
_sum = 0;
_sum_5 = 0;
_count = 0;
}
/// Set the state of this computer to the state recorded in the decoded JSON
/// [map].
void fromJson(Map<String, dynamic> map) {
_sum = map['sum'] as double;
_sum_5 = map['sum_5'] as double;
_count = map['count'] as int;
}
void printMean() {
print('Mean Reciprocal Rank \'$name\' (total = $count)');
print(
'mrr = ${mrr.toStringAsFixed(6)} '
'(inverse = ${(1 / mrr).toStringAsFixed(3)})',
);
print(
'mrr_5 = ${mrr_5.toStringAsFixed(6)} '
'(inverse = ${(1 / mrr_5).toStringAsFixed(3)})',
);
}
/// Return a map used to represent this computer in a JSON structure.
Map<String, dynamic> toJson() {
return {'sum': _sum, 'sum_5': _sum_5, 'count': _count};
}
}
/// A computer for calculating percentile-based metrics on a data set.
///
/// Specifically this tracks p50 (the median), p90, and p95.
///
/// See https://en.wikipedia.org/wiki/Percentile.
class PercentileComputer {
final String name;
/// The value limit allowed by this computer.
///
/// The computer can calculate percentile values for all data that range
/// between 0 and [valueLimit], exclusive.
int valueLimit;
/// An array of counts; the value at each index _i_ is the number of
/// occurrences of value _i_.
///
/// Any values larger than [valueLimit] are not counted here.
Uint32List _counts;
/// The number of values which are less than [valueLimit].
int valueCount = 0;
/// The number of values greater than [valueLimit].
int aboveValueMaxCount = 0;
List<int> aboveValueMaxSamples = [];
int maxValue = 0;
new(this.name, {required this.valueLimit}) : _counts = Uint32List(valueLimit);
/// Calculates the median (p50) value.
int get median => kthPercentile(50);
/// Calculates the p90 value; the value at the 90th percentile of the data.
int get p90 => kthPercentile(90);
/// Calculates the p95 value; the value at the 95th percentile of the data.
int get p95 => kthPercentile(95);
/// Add the data from the given [computer] to this computer.
void addData(PercentileComputer computer) {
if (computer.valueLimit != valueLimit) {
throw UnsupportedError(
'Cannot combine two PercentileComputers with different valueLimit '
'values',
);
}
for (var i = 0; i < _counts.length; i++) {
_counts[i] += computer._counts[i];
}
valueCount += computer.valueCount;
aboveValueMaxCount += computer.aboveValueMaxCount;
for (var val in computer.aboveValueMaxSamples) {
if (aboveValueMaxSamples.length < 10) {
aboveValueMaxSamples.add(val);
}
}
maxValue = math.max(maxValue, computer.maxValue);
}
void addValue(int val) {
if (val > valueLimit) {
aboveValueMaxCount++;
if (aboveValueMaxSamples.length < 10) {
aboveValueMaxSamples.add(val);
}
} else {
_counts[val]++;
valueCount++;
}
maxValue = math.max(maxValue, val);
}
void clear() {
_counts = Uint32List(0);
valueCount = 0;
aboveValueMaxCount = 0;
aboveValueMaxSamples = [];
maxValue = 0;
}
/// Set the state of this computer to the state recorded in the decoded JSON
/// [map].
void fromJson(Map<String, dynamic> map) {
valueLimit = map['valueLimit'] as int;
_counts = Uint32List.fromList((map['counts'] as List<dynamic>).cast<int>());
valueCount = map['valueCount'] as int;
aboveValueMaxCount = map['aboveValueMaxCount'] as int;
aboveValueMaxSamples = (map['aboveValueMaxSamples'] as List<dynamic>)
.cast<int>();
maxValue = map['maxValue'] as int;
}
/// Calculates the value at the _k_th percentile of the data.
int kthPercentile(int percentile) {
if (valueCount == 0) {
return 0;
}
// Linear walk through the data takes O([maxValue]) time. If this is too
// slow, a binary search can be implemented.
var targetIndex = valueCount * percentile / 100;
// The number of values represented by walking the counts.
var accumulation = 0;
for (var i = 0; i < _counts.length; i++) {
accumulation += _counts[i];
if (accumulation > targetIndex) {
// We've now accounted for [targetIndex] values, which includes the
// median value.
return i;
}
}
// The median value is in the very highest expected possible value.
return valueLimit;
}
/// Return a map used to represent this computer in a JSON structure.
Map<String, dynamic> toJson() {
return {
'counts': _counts,
'valueLimit': valueLimit,
'valueCount': valueCount,
'aboveValueMaxCount': aboveValueMaxCount,
'aboveValueMaxSamples': aboveValueMaxSamples,
'maxValue': maxValue,
};
}
}
/// An immutable class to represent the placement in some list, for example '2nd
/// place out of 5'.
class Place {
/// A 1-indexed place in a list
final int _numerator;
/// The total number of possible places.
final int _denominator;
const new(this._numerator, this._denominator)
: assert(_numerator > 0),
assert(_denominator >= _numerator);
/// Return an instance extracted from the decoded JSON [map].
factory fromJson(Map<String, dynamic> map) {
return Place(map['numerator'] as int, map['denominator'] as int);
}
const new none() : _numerator = 0, _denominator = 0;
int get denominator => _denominator;
@override
int get hashCode => Object.hash(_numerator, _denominator);
int get numerator => _numerator;
int get rank => _numerator;
@override
bool operator ==(Object other) =>
other is Place &&
_numerator == other._numerator &&
_denominator == other._denominator;
/// Return a map used to represent this place in a JSON structure.
Map<String, dynamic> toJson() {
return {'numerator': _numerator, 'denominator': _denominator};
}
}