<commit_before>// Copyright (c) 2019, 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 'package:analyzer/src/generated/utilities_general.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:collection/collection.dart';
import 'package:crypto/crypto.dart';
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/nnbd_migration.dart';
import 'package:nnbd_migration/src/front_end/offset_mapper.dart';
import 'package:nnbd_migration/src/front_end/unit_link.dart';
import 'package:nnbd_migration/src/preview/preview_site.dart';
import 'package:path/path.dart' as path;

/// A description of an edit that can be applied before rerunning the migration
/// in order to improve the migration results.
class EditDetail {
  /// A description of the edit that will be performed.
  final String description;

  /// The offset of the range to be replaced.
  final int offset;

  /// The length of the range to be replaced.
  final int length;

  /// The string with which the range will be replaced.
  final String replacement;

  /// Initialize a newly created detail.
  EditDetail(this.description, this.offset, this.length, this.replacement);

  /// Initializes a detail based on a [SourceEdit] object.
  factory EditDetail.fromSourceEdit(
          String description, SourceEdit sourceEdit) =>
      EditDetail(description, sourceEdit.offset, sourceEdit.length,
          sourceEdit.replacement);
}

/// Everything the front end needs to know to tell the server to perform a hint
/// action.
class HintAction {
  final HintActionKind kind;
  final int nodeId;
  HintAction(this.kind, this.nodeId);

  HintAction.fromJson(Map<String, Object> json)
      : nodeId = json['nodeId'] as int,
        kind = HintActionKind.values
            .singleWhere((action) => action.index == json['kind']);

  Map<String, Object> toJson() => {
        'nodeId': nodeId,
        'kind': kind.index,
      };
}

/// A class storing rendering information for an entire migration report.
///
/// This generally provides one [InstrumentationRenderer] (for one library)
/// with information about the rest of the libraries represented in the
/// instrumentation output.
class MigrationInfo {
  /// The information about the compilation units that are are migrated.
  final Set<UnitInfo> units;

  /// A map from file paths to the unit infos created for those files. The units
  /// in this map is a strict superset of the [units] that were migrated.
  final Map<String, UnitInfo> unitMap;

  /// The resource provider's path context.
  final path.Context pathContext;

  /// The filesystem root used to create relative paths for each unit.
  final String includedRoot;

  MigrationInfo(this.units, this.unitMap, this.pathContext, this.includedRoot);

  /// The path to the highlight.pack.js script, relative to [unitInfo].
  String get highlightJsPath => PreviewSite.highlightJsPath;

  /// The path to the highlight.pack.js stylesheet, relative to [unitInfo].
  String get highlightStylePath => PreviewSite.highlightCssPath;

  /// The path of the Dart logo displayed in the toolbar.
  String get dartLogoPath => PreviewSite.dartLogoPath;

  /// The path of the Material icons font.
  String get materialIconsPath => PreviewSite.materialIconsPath;

  /// The path of the Roboto font.
  String get robotoFont => PreviewSite.robotoFontPath;

  /// The path of the Roboto Mono font.
  String get robotoMonoFont => PreviewSite.robotoMonoFontPath;

  /// Return the path to [unit] from [includedRoot], to be used as a display
  /// name for a library.
  String computeName(UnitInfo unit) =>
      pathContext.relative(unit.path, from: includedRoot);

  List<UnitLink> unitLinks() {
    var links = <UnitLink>[];
    for (var unit in units) {
      var count = unit.fixRegions.length;
      links.add(
          UnitLink(unit.path, pathContext.split(computeName(unit)), count));
    }
    return links;
  }
}

/// A location from or to which a user might want to navigate.
abstract class NavigationRegion {
  /// The offset of the region.
  final int offset;

  /// The line number of the region.
  final int line;

  /// The length of the region.
  final int length;

  /// Initialize a newly created link.
  NavigationRegion(int offset, this.line, this.length)
      : assert(offset >= 0),
        offset = offset < 0 ? 0 : offset;
}

/// A location from which a user might want to navigate.
class NavigationSource extends NavigationRegion {
  /// The target to which the user should be navigated.
  final NavigationTarget target;

  /// Initialize a newly created link.
  NavigationSource(int offset, int line, int length, this.target)
      : super(offset, line, length);
}

/// A location to which a user might want to navigate.
class NavigationTarget extends NavigationRegion {
  /// The file containing the anchor.
  final String filePath;

  /// Initialize a newly created anchor.
  NavigationTarget(this.filePath, int offset, int line, int length)
      : super(offset, line, length);

  @override
  int get hashCode => JenkinsSmiHash.hash3(filePath.hashCode, offset, length);

  @override
  bool operator ==(other) {
    return other is NavigationTarget &&
        other.filePath == filePath &&
        other.offset == offset &&
        other.length == length;
  }

  @override
  String toString() => 'NavigationTarget["$filePath", $line, $offset, $length]';
}

/// A description of an explanation associated with a region of code that was
/// modified.
class RegionInfo {
  /// Type type of region.
  final RegionType regionType;

  /// The offset to the beginning of the region.
  final int offset;

  /// The length of the region.
  final int length;

  /// The line number of the beginning of the region.
  final int lineNumber;

  /// The explanation to be displayed for the region.
  final String explanation;

  /// The kind of fix that was applied.
  final NullabilityFixKind kind;

  /// Indicates whether this region should be counted in the edit summary.
  final bool isCounted;

  /// A list of the edits that are related to this range.
  List<EditDetail> edits;

  /// A list of the nullability propagation traces that are related to this
  /// range.
  List<TraceInfo> traces;

  /// Initialize a newly created region.
  RegionInfo(this.regionType, this.offset, this.length, this.lineNumber,
      this.explanation, this.kind, this.isCounted,
      {this.edits = const [], this.traces = const []});
}

/// Different types of regions that are called out.
enum RegionType {
  /// This is a region of code that was added in migration.
  add,

  /// This is a region of code that was removed in migration.
  remove,

  /// This is a region of code that wasn't changed by migration, but is being
  /// shown to give the user more information about the migration.
  informative,
}

/// Information about a single entry in a nullability trace.
class TraceEntryInfo {
  /// Text description of the entry.
  final String description;

  /// Name of the enclosing function, or `null` if not known.
  String function;

  /// Source code location associated with the entry, or `null` if no source
  /// code location is known.
  final NavigationTarget target;

  /// The hint actions available on this trace entry, or `[]` if none.
  final List<HintAction> hintActions;

  TraceEntryInfo(this.description, this.function, this.target,
      {this.hintActions = const []})
      : assert(hintActions != null);
}

/// Information about a nullability trace.
class TraceInfo {
  /// Text description of the trace.
  final String description;

  /// List of trace entries.
  final List<TraceEntryInfo> entries;

  TraceInfo(this.description, this.entries);
}

/// The migration information associated with a single compilation unit.
class UnitInfo {
  /// The absolute and normalized path of the unit.
  final String path;

  /// Hash of the original contents of the unit.
  List<int> _diskContentHash;

  /// The preview content of unit.
  String content;

  /// The information about the regions that have an explanation associated with
  /// them. The offsets in these regions are offsets into the post-edit content.
  final List<RegionInfo> regions = [];

  /// The navigation sources that are located in this file. The offsets in these
  /// sources are offsets into the pre-edit content.
  List<NavigationSource> sources;

  /// The navigation targets that are located in this file. The offsets in these
  /// targets are offsets into the pre-edit content.
  final Set<NavigationTarget> targets = {};

  /// An offset mapper reflecting changes made by the migration edits.
  OffsetMapper migrationOffsetMapper = OffsetMapper.identity;

  /// An offset mapper reflecting changes made to disk since the migration was
  /// run, which can be rebased on [migrationOffsetMapper] to create and
  /// maintain an offset mapper from current disk state to migration result.
  OffsetMapper diskChangesOffsetMapper = OffsetMapper.identity;

  /// Initialize a newly created unit.
  UnitInfo(this.path);

  /// Set the original/disk content of this file to later use [hadDiskContent].
  /// This does not have a getter because it is backed by a private hash.
  set diskContent(String originalContent) {
    _diskContentHash = md5.convert((originalContent ?? '').codeUnits).bytes;
  }

  /// Returns the [regions] that represent a fixed (changed) region of code.
  List<RegionInfo> get fixRegions => regions
      .where((region) => region.regionType != RegionType.informative)
      .toList();

  /// Returns the [regions] that are informative.
  List<RegionInfo> get informativeRegions => regions
      .where((region) => region.regionType == RegionType.informative)
      .toList();

  /// The object used to map the pre-edit offsets in the navigation targets to
  /// the post-edit offsets in the [content].
  OffsetMapper get offsetMapper =>
      OffsetMapper.rebase(diskChangesOffsetMapper, migrationOffsetMapper);

  /// Check if this unit's file had expected disk contents [checkContent].
  bool hadDiskContent(String checkContent) {
    assert(_diskContentHash != null);
    return const ListEquality().equals(
        _diskContentHash, md5.convert((checkContent ?? '').codeUnits).bytes);
  }

  void handleSourceEdit(SourceEdit sourceEdit) {
    final contentCopy = content;
    final regionsCopy = List<RegionInfo>.from(regions);
    final insertLength = sourceEdit.replacement.length;
    final deleteLength = sourceEdit.length;
    final migratedOffset = offsetMapper.map(sourceEdit.offset);
    final diskOffset = diskChangesOffsetMapper.map(sourceEdit.offset);
    if (migratedOffset == null || diskOffset == null) {
      throw StateError('cannot apply replacement, offset has been deleted.');
    }
    try {
      content = content.replaceRange(migratedOffset,
          migratedOffset + deleteLength, sourceEdit.replacement);
      regions.clear();
      regions.addAll(regionsCopy
          .where((region) => region.offset + region.length <= migratedOffset));
      regions.addAll(regionsCopy
          .where((region) => region.offset >= migratedOffset + deleteLength)
          .map((region) => RegionInfo(
              region.regionType,
              // TODO: perhaps this should be handled by offset mapper instead,
              // since offset mapper handles navigation, edits, and traces, and
              // this is the odd ball out.
              region.offset + insertLength - deleteLength,
              region.length,
              region.lineNumber,
              region.explanation,
              region.kind,
              region.isCounted,
              edits: region.edits,
              traces: region.traces)));

      diskChangesOffsetMapper = OffsetMapper.sequence(
          diskChangesOffsetMapper,
          OffsetMapper.forReplacement(
              diskOffset, deleteLength, sourceEdit.replacement));
    } catch (e) {
      regions.clear();
      regions.addAll(regionsCopy);
      content = contentCopy;
      rethrow;
    }
  }

  /// Returns the [RegionInfo] at offset [offset].
  // TODO(srawlins): This is O(n), used each time the user clicks on a region.
  //  Consider changing the type of [regions] to facilitate O(1) searching.
  RegionInfo regionAt(int offset) =>
      regions.firstWhere((region) => region.offset == offset);
}
<commit_msg>Sort declarations in migration_info.dart<commit_after>// Copyright (c) 2019, 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 'package:analyzer/src/generated/utilities_general.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:collection/collection.dart';
import 'package:crypto/crypto.dart';
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/nnbd_migration.dart';
import 'package:nnbd_migration/src/front_end/offset_mapper.dart';
import 'package:nnbd_migration/src/front_end/unit_link.dart';
import 'package:nnbd_migration/src/preview/preview_site.dart';
import 'package:path/path.dart' as path;

/// A description of an edit that can be applied before rerunning the migration
/// in order to improve the migration results.
class EditDetail {
  /// A description of the edit that will be performed.
  final String description;

  /// The offset of the range to be replaced.
  final int offset;

  /// The length of the range to be replaced.
  final int length;

  /// The string with which the range will be replaced.
  final String replacement;

  /// Initialize a newly created detail.
  EditDetail(this.description, this.offset, this.length, this.replacement);

  /// Initializes a detail based on a [SourceEdit] object.
  factory EditDetail.fromSourceEdit(
          String description, SourceEdit sourceEdit) =>
      EditDetail(description, sourceEdit.offset, sourceEdit.length,
          sourceEdit.replacement);
}

/// Everything the front end needs to know to tell the server to perform a hint
/// action.
class HintAction {
  final HintActionKind kind;
  final int nodeId;
  HintAction(this.kind, this.nodeId);

  HintAction.fromJson(Map<String, Object> json)
      : nodeId = json['nodeId'] as int,
        kind = HintActionKind.values
            .singleWhere((action) => action.index == json['kind']);

  Map<String, Object> toJson() => {
        'nodeId': nodeId,
        'kind': kind.index,
      };
}

/// A class storing rendering information for an entire migration report.
///
/// This generally provides one [InstrumentationRenderer] (for one library)
/// with information about the rest of the libraries represented in the
/// instrumentation output.
class MigrationInfo {
  /// The information about the compilation units that are are migrated.
  final Set<UnitInfo> units;

  /// A map from file paths to the unit infos created for those files. The units
  /// in this map is a strict superset of the [units] that were migrated.
  final Map<String, UnitInfo> unitMap;

  /// The resource provider's path context.
  final path.Context pathContext;

  /// The filesystem root used to create relative paths for each unit.
  final String includedRoot;

  MigrationInfo(this.units, this.unitMap, this.pathContext, this.includedRoot);

  /// The path of the Dart logo displayed in the toolbar.
  String get dartLogoPath => PreviewSite.dartLogoPath;

  /// The path to the highlight.pack.js script, relative to [unitInfo].
  String get highlightJsPath => PreviewSite.highlightJsPath;

  /// The path to the highlight.pack.js stylesheet, relative to [unitInfo].
  String get highlightStylePath => PreviewSite.highlightCssPath;

  /// The path of the Material icons font.
  String get materialIconsPath => PreviewSite.materialIconsPath;

  /// The path of the Roboto font.
  String get robotoFont => PreviewSite.robotoFontPath;

  /// The path of the Roboto Mono font.
  String get robotoMonoFont => PreviewSite.robotoMonoFontPath;

  /// Return the path to [unit] from [includedRoot], to be used as a display
  /// name for a library.
  String computeName(UnitInfo unit) =>
      pathContext.relative(unit.path, from: includedRoot);

  List<UnitLink> unitLinks() {
    var links = <UnitLink>[];
    for (var unit in units) {
      var count = unit.fixRegions.length;
      links.add(
          UnitLink(unit.path, pathContext.split(computeName(unit)), count));
    }
    return links;
  }
}

/// A location from or to which a user might want to navigate.
abstract class NavigationRegion {
  /// The offset of the region.
  final int offset;

  /// The line number of the region.
  final int line;

  /// The length of the region.
  final int length;

  /// Initialize a newly created link.
  NavigationRegion(int offset, this.line, this.length)
      : assert(offset >= 0),
        offset = offset < 0 ? 0 : offset;
}

/// A location from which a user might want to navigate.
class NavigationSource extends NavigationRegion {
  /// The target to which the user should be navigated.
  final NavigationTarget target;

  /// Initialize a newly created link.
  NavigationSource(int offset, int line, int length, this.target)
      : super(offset, line, length);
}

/// A location to which a user might want to navigate.
class NavigationTarget extends NavigationRegion {
  /// The file containing the anchor.
  final String filePath;

  /// Initialize a newly created anchor.
  NavigationTarget(this.filePath, int offset, int line, int length)
      : super(offset, line, length);

  @override
  int get hashCode => JenkinsSmiHash.hash3(filePath.hashCode, offset, length);

  @override
  bool operator ==(other) {
    return other is NavigationTarget &&
        other.filePath == filePath &&
        other.offset == offset &&
        other.length == length;
  }

  @override
  String toString() => 'NavigationTarget["$filePath", $line, $offset, $length]';
}

/// A description of an explanation associated with a region of code that was
/// modified.
class RegionInfo {
  /// Type type of region.
  final RegionType regionType;

  /// The offset to the beginning of the region.
  final int offset;

  /// The length of the region.
  final int length;

  /// The line number of the beginning of the region.
  final int lineNumber;

  /// The explanation to be displayed for the region.
  final String explanation;

  /// The kind of fix that was applied.
  final NullabilityFixKind kind;

  /// Indicates whether this region should be counted in the edit summary.
  final bool isCounted;

  /// A list of the edits that are related to this range.
  List<EditDetail> edits;

  /// A list of the nullability propagation traces that are related to this
  /// range.
  List<TraceInfo> traces;

  /// Initialize a newly created region.
  RegionInfo(this.regionType, this.offset, this.length, this.lineNumber,
      this.explanation, this.kind, this.isCounted,
      {this.edits = const [], this.traces = const []});
}

/// Different types of regions that are called out.
enum RegionType {
  /// This is a region of code that was added in migration.
  add,

  /// This is a region of code that was removed in migration.
  remove,

  /// This is a region of code that wasn't changed by migration, but is being
  /// shown to give the user more information about the migration.
  informative,
}

/// Information about a single entry in a nullability trace.
class TraceEntryInfo {
  /// Text description of the entry.
  final String description;

  /// Name of the enclosing function, or `null` if not known.
  String function;

  /// Source code location associated with the entry, or `null` if no source
  /// code location is known.
  final NavigationTarget target;

  /// The hint actions available on this trace entry, or `[]` if none.
  final List<HintAction> hintActions;

  TraceEntryInfo(this.description, this.function, this.target,
      {this.hintActions = const []})
      : assert(hintActions != null);
}

/// Information about a nullability trace.
class TraceInfo {
  /// Text description of the trace.
  final String description;

  /// List of trace entries.
  final List<TraceEntryInfo> entries;

  TraceInfo(this.description, this.entries);
}

/// The migration information associated with a single compilation unit.
class UnitInfo {
  /// The absolute and normalized path of the unit.
  final String path;

  /// Hash of the original contents of the unit.
  List<int> _diskContentHash;

  /// The preview content of unit.
  String content;

  /// The information about the regions that have an explanation associated with
  /// them. The offsets in these regions are offsets into the post-edit content.
  final List<RegionInfo> regions = [];

  /// The navigation sources that are located in this file. The offsets in these
  /// sources are offsets into the pre-edit content.
  List<NavigationSource> sources;

  /// The navigation targets that are located in this file. The offsets in these
  /// targets are offsets into the pre-edit content.
  final Set<NavigationTarget> targets = {};

  /// An offset mapper reflecting changes made by the migration edits.
  OffsetMapper migrationOffsetMapper = OffsetMapper.identity;

  /// An offset mapper reflecting changes made to disk since the migration was
  /// run, which can be rebased on [migrationOffsetMapper] to create and
  /// maintain an offset mapper from current disk state to migration result.
  OffsetMapper diskChangesOffsetMapper = OffsetMapper.identity;

  /// Initialize a newly created unit.
  UnitInfo(this.path);

  /// Set the original/disk content of this file to later use [hadDiskContent].
  /// This does not have a getter because it is backed by a private hash.
  set diskContent(String originalContent) {
    _diskContentHash = md5.convert((originalContent ?? '').codeUnits).bytes;
  }

  /// Returns the [regions] that represent a fixed (changed) region of code.
  List<RegionInfo> get fixRegions => regions
      .where((region) => region.regionType != RegionType.informative)
      .toList();

  /// Returns the [regions] that are informative.
  List<RegionInfo> get informativeRegions => regions
      .where((region) => region.regionType == RegionType.informative)
      .toList();

  /// The object used to map the pre-edit offsets in the navigation targets to
  /// the post-edit offsets in the [content].
  OffsetMapper get offsetMapper =>
      OffsetMapper.rebase(diskChangesOffsetMapper, migrationOffsetMapper);

  /// Check if this unit's file had expected disk contents [checkContent].
  bool hadDiskContent(String checkContent) {
    assert(_diskContentHash != null);
    return const ListEquality().equals(
        _diskContentHash, md5.convert((checkContent ?? '').codeUnits).bytes);
  }

  void handleSourceEdit(SourceEdit sourceEdit) {
    final contentCopy = content;
    final regionsCopy = List<RegionInfo>.from(regions);
    final insertLength = sourceEdit.replacement.length;
    final deleteLength = sourceEdit.length;
    final migratedOffset = offsetMapper.map(sourceEdit.offset);
    final diskOffset = diskChangesOffsetMapper.map(sourceEdit.offset);
    if (migratedOffset == null || diskOffset == null) {
      throw StateError('cannot apply replacement, offset has been deleted.');
    }
    try {
      content = content.replaceRange(migratedOffset,
          migratedOffset + deleteLength, sourceEdit.replacement);
      regions.clear();
      regions.addAll(regionsCopy
          .where((region) => region.offset + region.length <= migratedOffset));
      regions.addAll(regionsCopy
          .where((region) => region.offset >= migratedOffset + deleteLength)
          .map((region) => RegionInfo(
              region.regionType,
              // TODO: perhaps this should be handled by offset mapper instead,
              // since offset mapper handles navigation, edits, and traces, and
              // this is the odd ball out.
              region.offset + insertLength - deleteLength,
              region.length,
              region.lineNumber,
              region.explanation,
              region.kind,
              region.isCounted,
              edits: region.edits,
              traces: region.traces)));

      diskChangesOffsetMapper = OffsetMapper.sequence(
          diskChangesOffsetMapper,
          OffsetMapper.forReplacement(
              diskOffset, deleteLength, sourceEdit.replacement));
    } catch (e) {
      regions.clear();
      regions.addAll(regionsCopy);
      content = contentCopy;
      rethrow;
    }
  }

  /// Returns the [RegionInfo] at offset [offset].
  // TODO(srawlins): This is O(n), used each time the user clicks on a region.
  //  Consider changing the type of [regions] to facilitate O(1) searching.
  RegionInfo regionAt(int offset) =>
      regions.firstWhere((region) => region.offset == offset);
}

{'fraction_non_alphanumeric': 0.09462649541871725, 'fraction_numerical': 0.0008802464690113232, 'mean_word_length': 3.1663610601766963, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3746693', 'n_tokens_mistral': 6818, 'n_tokens_neox': 6566, 'n_words': 2563}