Skip to content

Commit 19d0d88

Browse files
keertipCommit Bot
authored and
Commit Bot
committed
Add support for import rename
Change-Id: I7358e697d022302f060eb751415c28087c9642a4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/245741 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Keerti Parthasarathy <[email protected]>
1 parent 0340696 commit 19d0d88

File tree

5 files changed

+337
-130
lines changed

5 files changed

+337
-130
lines changed

pkg/analysis_server/lib/src/cider/rename.dart

Lines changed: 104 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class CanRenameResponse {
5353
} else if (element is ConstructorElement) {
5454
status = validateConstructorName(name);
5555
_analyzePossibleConflicts(element, status, name);
56+
} else if (element is ImportElement) {
57+
status = validateImportPrefixName(name);
5658
}
5759

5860
if (status == null) {
@@ -125,34 +127,9 @@ class CheckNameResponse {
125127
for (var element in elements) {
126128
matches.addAll(await fileResolver.findReferences2(element));
127129
}
128-
129130
FlutterWidgetRename? flutterRename;
130-
var flutterState = canRename._flutterWidgetState;
131-
if (flutterState != null) {
132-
var stateClass = flutterState.state;
133-
var stateName = flutterState.newName;
134-
var match = await fileResolver.findReferences2(stateClass);
135-
var sourcePath = stateClass.source.fullName;
136-
var location = stateClass.enclosingElement.lineInfo
137-
.getLocation(stateClass.nameOffset);
138-
CiderSearchMatch ciderMatch;
139-
var searchInfo = CiderSearchInfo(
140-
location, stateClass.nameLength, MatchKind.DECLARATION);
141-
try {
142-
ciderMatch = match.firstWhere((m) => m.path == sourcePath);
143-
ciderMatch.references.add(searchInfo);
144-
} catch (_) {
145-
match.add(CiderSearchMatch(sourcePath, [], [searchInfo]));
146-
}
147-
var replacements = match
148-
.map((m) => CiderReplaceMatch(
149-
m.path,
150-
m.references
151-
.map((p) => ReplaceInfo(
152-
stateName, p.startPosition, stateClass.nameLength))
153-
.toList()))
154-
.toList();
155-
flutterRename = FlutterWidgetRename(stateName, match, replacements);
131+
if (canRename._flutterWidgetState != null) {
132+
flutterRename = await _computeFlutterStateName();
156133
}
157134
var replaceMatches = <CiderReplaceMatch>[];
158135
if (element is ConstructorElement) {
@@ -179,6 +156,30 @@ class CheckNameResponse {
179156
replaceMatches.addMatch(result.path, result.matches.toList());
180157
}
181158
}
159+
} else if (element is ImportElement) {
160+
var replaceInfo = <ReplaceInfo>[];
161+
for (var match in matches) {
162+
for (var ref in match.references) {
163+
if (newName.isEmpty) {
164+
replaceInfo.add(ReplaceInfo('', ref.startPosition, ref.length));
165+
} else {
166+
var identifier = await _getInterpolationIdentifier(
167+
match.path, ref.startPosition);
168+
if (identifier != null) {
169+
var lineInfo = canRename.lineInfo;
170+
replaceInfo.add(ReplaceInfo('{$newName.${identifier.name}}',
171+
lineInfo.getLocation(identifier.offset), identifier.length));
172+
} else {
173+
replaceInfo
174+
.add(ReplaceInfo('$newName.', ref.startPosition, ref.length));
175+
}
176+
}
177+
}
178+
replaceMatches.addMatch(match.path, replaceInfo);
179+
var sourcePath = element.source.fullName;
180+
var infos = await _addElementDeclaration(element, sourcePath);
181+
replaceMatches.addMatch(sourcePath, infos);
182+
}
182183
} else {
183184
for (var match in matches) {
184185
replaceMatches.addMatch(
@@ -213,6 +214,32 @@ class CheckNameResponse {
213214
lineInfo.getLocation(element.setter!.nameOffset),
214215
element.setter!.nameLength));
215216
}
217+
} else if (element is ImportElement) {
218+
var prefix = element.prefix;
219+
var unit =
220+
(await canRename._fileResolver.resolve2(path: sourcePath)).unit;
221+
var index = element.library.imports.indexOf(element);
222+
var node = unit.directives.whereType<ImportDirective>().elementAt(index);
223+
if (newName.isEmpty) {
224+
// We should not get `prefix == null` because we check in
225+
// `checkNewName` that the new name is different.
226+
if (prefix == null) {
227+
return infos;
228+
}
229+
var prefixEnd = prefix.nameOffset + prefix.nameLength;
230+
infos.add(ReplaceInfo(newName, lineInfo.getLocation(node.uri.end),
231+
prefixEnd - node.uri.end));
232+
} else {
233+
if (prefix == null) {
234+
var uriEnd = node.uri.end;
235+
infos.add(
236+
ReplaceInfo(' as $newName', lineInfo.getLocation(uriEnd), 0));
237+
} else {
238+
var offset = prefix.nameOffset;
239+
var length = prefix.nameLength;
240+
infos.add(ReplaceInfo(newName, lineInfo.getLocation(offset), length));
241+
}
242+
}
216243
} else {
217244
var location = (await canRename._fileResolver.resolve2(path: sourcePath))
218245
.lineInfo
@@ -222,6 +249,53 @@ class CheckNameResponse {
222249
return infos;
223250
}
224251

252+
Future<FlutterWidgetRename?> _computeFlutterStateName() async {
253+
var flutterState = canRename._flutterWidgetState;
254+
var stateClass = flutterState!.state;
255+
var stateName = flutterState.newName;
256+
var match = await canRename._fileResolver.findReferences2(stateClass);
257+
var sourcePath = stateClass.source.fullName;
258+
var location =
259+
stateClass.enclosingElement.lineInfo.getLocation(stateClass.nameOffset);
260+
CiderSearchMatch ciderMatch;
261+
var searchInfo =
262+
CiderSearchInfo(location, stateClass.nameLength, MatchKind.DECLARATION);
263+
try {
264+
ciderMatch = match.firstWhere((m) => m.path == sourcePath);
265+
ciderMatch.references.add(searchInfo);
266+
} catch (_) {
267+
match.add(CiderSearchMatch(sourcePath, [searchInfo]));
268+
}
269+
var replacements = match
270+
.map((m) => CiderReplaceMatch(
271+
m.path,
272+
m.references
273+
.map((p) => ReplaceInfo(
274+
stateName, p.startPosition, stateClass.nameLength))
275+
.toList()))
276+
.toList();
277+
return FlutterWidgetRename(stateName, match, replacements);
278+
}
279+
280+
/// If the given [reference] is before an interpolated [SimpleIdentifier] in
281+
/// an [InterpolationExpression] without surrounding curly brackets, return
282+
/// it. Otherwise return `null`.
283+
Future<SimpleIdentifier?> _getInterpolationIdentifier(
284+
String path, CharacterLocation loc) async {
285+
var resolvedUnit = await canRename._fileResolver.resolve2(path: path);
286+
var lineInfo = resolvedUnit.lineInfo;
287+
var node = NodeLocator(
288+
lineInfo.getOffsetOfLine(loc.lineNumber - 1) + loc.columnNumber)
289+
.searchWithin(resolvedUnit.unit);
290+
if (node is SimpleIdentifier) {
291+
var parent = node.parent;
292+
if (parent is InterpolationExpression && parent.rightBracket == null) {
293+
return node;
294+
}
295+
}
296+
return null;
297+
}
298+
225299
Future<CiderReplaceMatch?> _replaceSyntheticConstructor() async {
226300
var element = canRename.refactoringElement.element;
227301
var classElement = element.enclosingElement;
@@ -313,6 +387,9 @@ class CiderRenameComputer {
313387
if (element is ConstructorElement) {
314388
return true;
315389
}
390+
if (element is ImportElement) {
391+
return true;
392+
}
316393
if (element is LabelElement || element is LocalElement) {
317394
return true;
318395
}

pkg/analysis_server/test/src/cider/rename_test.dart

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,131 @@ void f() {
651651
[ReplaceInfo('bar', CharacterLocation(1, 1), 3)]);
652652
}
653653

654+
void test_rename_import() async {
655+
var testCode = '''
656+
import 'dart:async';
657+
^import 'dart:math' show Random, min hide max;
658+
void f() {
659+
Future f;
660+
Random r;
661+
min(1, 2);
662+
}
663+
''';
664+
665+
var result = await _rename(testCode, 'newName');
666+
_assertTestChangeResult('''
667+
import 'dart:async';
668+
import 'dart:math' as newName show Random, min hide max;
669+
void f() {
670+
Future f;
671+
newName.Random r;
672+
newName.min(1, 2);
673+
}
674+
''', result!.replaceMatches.first.matches);
675+
}
676+
677+
void test_rename_import_hasCurlyBrackets() async {
678+
var testCode = r'''
679+
// test
680+
^import 'dart:async';
681+
void f() {
682+
Future f;
683+
print('Future type: ${Future}');
684+
}
685+
''';
686+
687+
var result = await _rename(testCode, 'newName');
688+
_assertTestChangeResult(r'''
689+
// test
690+
import 'dart:async' as newName;
691+
void f() {
692+
newName.Future f;
693+
print('Future type: ${newName.Future}');
694+
}
695+
''', result!.replaceMatches.first.matches);
696+
}
697+
698+
void test_rename_import_noCurlyBrackets() async {
699+
var testCode = r'''
700+
// test
701+
^import 'dart:async';
702+
void f() {
703+
Future f;
704+
print('Future type: $Future');
705+
}
706+
''';
707+
var result = await _rename(testCode, 'newName');
708+
_assertTestChangeResult(r'''
709+
// test
710+
import 'dart:async' as newName;
711+
void f() {
712+
newName.Future f;
713+
print('Future type: ${newName.Future}');
714+
}
715+
''', result!.replaceMatches.first.matches);
716+
}
717+
718+
void test_rename_import_onPrefixElement() async {
719+
var testCode = '''
720+
import 'dart:async' as test;
721+
import 'dart:math' as test;
722+
void f() {
723+
test.Future f;
724+
^test.pi;
725+
test.e;
726+
}
727+
''';
728+
var result = await _rename(testCode, 'newName');
729+
_assertTestChangeResult('''
730+
import 'dart:async' as test;
731+
import 'dart:math' as newName;
732+
void f() {
733+
test.Future f;
734+
newName.pi;
735+
newName.e;
736+
}
737+
''', result!.replaceMatches.first.matches);
738+
}
739+
740+
void test_rename_import_prefix() async {
741+
var testCode = '''
742+
import 'dart:math' as test;
743+
^import 'dart:async' as test;
744+
void f() {
745+
test.max(1, 2);
746+
test.Future f;
747+
}
748+
''';
749+
var result = await _rename(testCode, 'newName');
750+
_assertTestChangeResult('''
751+
import 'dart:math' as test;
752+
import 'dart:async' as newName;
753+
void f() {
754+
test.max(1, 2);
755+
newName.Future f;
756+
}
757+
''', result!.replaceMatches.first.matches);
758+
}
759+
760+
void test_rename_import_remove_prefix() async {
761+
var testCode = '''
762+
import 'dart:math' as test;
763+
^import 'dart:async' as test;
764+
void f() {
765+
test.Future f;
766+
}
767+
''';
768+
769+
var result = await _rename(testCode, '');
770+
_assertTestChangeResult('''
771+
import 'dart:math' as test;
772+
import 'dart:async';
773+
void f() {
774+
Future f;
775+
}
776+
''', result!.replaceMatches.first.matches);
777+
}
778+
654779
void test_rename_local() async {
655780
var testCode = '''
656781
void foo() {

0 commit comments

Comments
 (0)