mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:37:43 +00:00
Add a way to map navigation target offsets to the edited content
Change-Id: Ia5e10cbc1a3510e6764fa67e5e6d47c9f19dbc48 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/119165 Reviewed-by: Samuel Rawlins <srawlins@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
605422c94e
commit
0829212a2a
|
@ -6,6 +6,7 @@ import 'package:analysis_server/src/analysis_server.dart';
|
|||
import 'package:analysis_server/src/edit/fix/dartfix_listener.dart';
|
||||
import 'package:analysis_server/src/edit/nnbd_migration/instrumentation_information.dart';
|
||||
import 'package:analysis_server/src/edit/nnbd_migration/migration_info.dart';
|
||||
import 'package:analysis_server/src/edit/nnbd_migration/offset_mapper.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
import 'package:analyzer/dart/analysis/session.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
|
@ -123,17 +124,7 @@ class InfoBuilder {
|
|||
List<RegionInfo> regions = unitInfo.regions;
|
||||
List<SourceEdit> edits = fileEdit.edits;
|
||||
edits.sort((first, second) => first.offset.compareTo(second.offset));
|
||||
// Compute the deltas for the regions that will be computed as we apply the
|
||||
// edits. We need the deltas because the offsets to the regions are relative
|
||||
// to the edited source, but the edits are being applied in reverse order so
|
||||
// the offset in the pre-edited source will not match the offset in the
|
||||
// post-edited source. The deltas compensate for that difference.
|
||||
List<int> deltas = [];
|
||||
int previousDelta = 0;
|
||||
for (SourceEdit edit in edits) {
|
||||
deltas.add(previousDelta);
|
||||
previousDelta += (edit.replacement.length - edit.length);
|
||||
}
|
||||
OffsetMapper mapper = OffsetMapper.forEdits(edits);
|
||||
// Apply edits in reverse order and build the regions.
|
||||
int index = edits.length - 1;
|
||||
for (SourceEdit edit in edits.reversed) {
|
||||
|
@ -141,19 +132,20 @@ class InfoBuilder {
|
|||
int length = edit.length;
|
||||
String replacement = edit.replacement;
|
||||
int end = offset + length;
|
||||
int delta = deltas[index--];
|
||||
// Insert the replacement text without deleting the replaced text.
|
||||
content = content.replaceRange(end, end, replacement);
|
||||
FixInfo fixInfo = _findFixInfo(sourceInfo, offset);
|
||||
String explanation = '${fixInfo.fix.description.appliedMessage}.';
|
||||
List<RegionDetail> details = _computeDetails(fixInfo);
|
||||
if (length > 0) {
|
||||
regions.add(RegionInfo(offset + delta, length, explanation, details));
|
||||
regions.add(
|
||||
RegionInfo(mapper.map(offset), length, explanation, details));
|
||||
}
|
||||
regions.add(
|
||||
RegionInfo(end + delta, replacement.length, explanation, details));
|
||||
regions.add(RegionInfo(
|
||||
mapper.map(end), replacement.length, explanation, details));
|
||||
}
|
||||
regions.sort((first, second) => first.offset.compareTo(second.offset));
|
||||
unitInfo.offsetMapper = mapper;
|
||||
}
|
||||
unitInfo.content = content;
|
||||
return unitInfo;
|
||||
|
@ -173,7 +165,7 @@ class InfoBuilder {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Return the navigation target
|
||||
/// Return the navigation target corresponding to the given [origin].
|
||||
NavigationTarget _targetFor(EdgeOriginInfo origin) {
|
||||
AstNode node = origin.node;
|
||||
String filePath = origin.source.fullName;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// 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:analysis_server/src/edit/nnbd_migration/offset_mapper.dart';
|
||||
import 'package:analyzer/src/generated/utilities_general.dart';
|
||||
|
||||
/// The migration information associated with a single library.
|
||||
|
@ -81,12 +82,17 @@ class UnitInfo {
|
|||
String content;
|
||||
|
||||
/// The information about the regions that have an explanation associated with
|
||||
/// them.
|
||||
/// them. The offsets in these regions are offsets into the post-edit content.
|
||||
final List<RegionInfo> regions = [];
|
||||
|
||||
/// The navigation targets that are located in this file.
|
||||
/// 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 = {};
|
||||
|
||||
/// The object used to map the pre-edit offsets in the navigation targets to
|
||||
/// the post-edit offsets in the [content].
|
||||
OffsetMapper offsetMapper = OffsetMapper.identity;
|
||||
|
||||
/// Initialize a newly created unit.
|
||||
UnitInfo(this.path);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
// 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_plugin/protocol/protocol_common.dart';
|
||||
|
||||
/// An object that can map the offsets before a sequence of edits to the offsets
|
||||
/// after applying the edits.
|
||||
abstract class OffsetMapper {
|
||||
/// A mapper used for files that were not modified.
|
||||
static OffsetMapper identity = _IdentityMapper();
|
||||
|
||||
/// Return a mapper representing the file modified by the given [edits].
|
||||
factory OffsetMapper.forEdits(List<SourceEdit> edits) => _EditMapper(edits);
|
||||
|
||||
/// Return the post-edit offset that corresponds to the given pre-edit
|
||||
/// [offset].
|
||||
int map(int offset);
|
||||
}
|
||||
|
||||
/// A mapper used for files that were modified by a set of edits.
|
||||
class _EditMapper implements OffsetMapper {
|
||||
/// A list whose elements are the highest pre-edit offset for which the
|
||||
/// corresponding element of [_deltas] should be applied.
|
||||
List<int> _offsets = [];
|
||||
|
||||
/// A list whose elements are the deltas to be applied for all pre-edit
|
||||
/// offsets that are less than or equal to the corresponding element of
|
||||
/// [_offsets].
|
||||
List<int> _deltas = [];
|
||||
|
||||
/// Initialize a newly created mapper based on the given set of [edits].
|
||||
_EditMapper(List<SourceEdit> edits) {
|
||||
_initializeDeltas(edits);
|
||||
}
|
||||
|
||||
@override
|
||||
int map(int offset) => offset + _deltaFor(offset);
|
||||
|
||||
/// Return the delta to be added to the pre-edit [offset] to produce the
|
||||
/// post-edit offset.
|
||||
int _deltaFor(int offset) {
|
||||
for (int i = 0; i < _offsets.length; i++) {
|
||||
int currentOffset = _offsets[i];
|
||||
if (currentOffset >= offset || currentOffset < 0) {
|
||||
return _deltas[i];
|
||||
}
|
||||
}
|
||||
// We should never get here because [_initializeDeltas] always adds an
|
||||
// offset/delta pair at the end of the list whose offset is less than zero.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Initialize the list of old offsets and deltas used by [_deltaFor].
|
||||
void _initializeDeltas(List<SourceEdit> edits) {
|
||||
int previousDelta = 0;
|
||||
for (SourceEdit edit in edits) {
|
||||
int offset = edit.offset;
|
||||
int length = edit.length;
|
||||
_offsets.add(offset);
|
||||
_deltas.add(previousDelta);
|
||||
previousDelta += (edit.replacement.length - length);
|
||||
}
|
||||
_offsets.add(-1);
|
||||
_deltas.add(previousDelta);
|
||||
}
|
||||
}
|
||||
|
||||
/// A mapper used for files that were not modified.
|
||||
class _IdentityMapper implements OffsetMapper {
|
||||
@override
|
||||
int map(int offset) => offset;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// 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:analysis_server/src/edit/nnbd_migration/offset_mapper.dart';
|
||||
import 'package:analysis_server/src/protocol_server.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../../../analysis_abstract.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(OffsetMapperTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class OffsetMapperTest extends AbstractAnalysisTest {
|
||||
test_identity() {
|
||||
OffsetMapper mapper = OffsetMapper.identity;
|
||||
expect(mapper.map(0), 0);
|
||||
expect(mapper.map(20), 20);
|
||||
expect(mapper.map(0xFFFFFF), 0xFFFFFF);
|
||||
}
|
||||
|
||||
test_multipleEdits() {
|
||||
OffsetMapper mapper = OffsetMapper.forEdits([
|
||||
SourceEdit(13, 0, '?'),
|
||||
SourceEdit(21, 0, '!'),
|
||||
SourceEdit(32, 0, '?'),
|
||||
]);
|
||||
expect(mapper.map(0), 0);
|
||||
expect(mapper.map(13), 13);
|
||||
expect(mapper.map(14), 15);
|
||||
expect(mapper.map(21), 22);
|
||||
expect(mapper.map(22), 24);
|
||||
expect(mapper.map(32), 34);
|
||||
expect(mapper.map(33), 36);
|
||||
expect(mapper.map(55), 58);
|
||||
}
|
||||
|
||||
test_singleEdit() {
|
||||
OffsetMapper mapper = OffsetMapper.forEdits([
|
||||
SourceEdit(13, 0, '?'),
|
||||
]);
|
||||
expect(mapper.map(0), 0);
|
||||
expect(mapper.map(13), 13);
|
||||
expect(mapper.map(14), 15);
|
||||
}
|
||||
}
|
|
@ -5,9 +5,11 @@
|
|||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'info_builder_test.dart' as info_builder;
|
||||
import 'offset_mapper_test.dart' as offset_mapper;
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
info_builder.main();
|
||||
offset_mapper.main();
|
||||
}, name: 'nnbd_migration');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue