blob: 755ebffb7bb14e98ea2b9db3be7f6776c20077ae [file] [log] [blame]
// Copyright (c) 2014, 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:async';
import 'package:analysis_server/src/protocol_server.dart' hide Element;
import 'package:analysis_server/src/services/correction/status.dart';
import 'package:analysis_server/src/services/correction/util.dart';
import 'package:analysis_server/src/services/refactoring/naming_conventions.dart';
import 'package:analysis_server/src/services/refactoring/refactoring.dart';
import 'package:analysis_server/src/services/refactoring/refactoring_internal.dart';
import 'package:analysis_server/src/services/refactoring/rename.dart';
import 'package:analysis_server/src/services/search/hierarchy.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
import 'package:analysis_server/src/services/search/search_engine_internal.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/ast_provider.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
/**
* A [Refactoring] for renaming [ConstructorElement]s.
*/
class RenameConstructorRefactoringImpl extends RenameRefactoringImpl {
final AstProvider astProvider;
RenameConstructorRefactoringImpl(RefactoringWorkspace workspace,
this.astProvider, ConstructorElement element)
: super(workspace, element);
@override
ConstructorElement get element => super.element as ConstructorElement;
@override
String get refactoringName {
return "Rename Constructor";
}
@override
Future<RefactoringStatus> checkFinalConditions() {
RefactoringStatus result = new RefactoringStatus();
return new Future.value(result);
}
@override
RefactoringStatus checkNewName() {
RefactoringStatus result = super.checkNewName();
result.addStatus(validateConstructorName(newName));
if (newName != null) {
_analyzePossibleConflicts(result);
}
return result;
}
@override
Future<void> fillChange() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
// prepare references
List<SearchMatch> matches = await searchEngine.searchReferences(element);
List<SourceReference> references = getSourceReferences(matches);
// append declaration
if (element.isSynthetic) {
await _replaceSynthetic();
} else {
references.add(_createDeclarationReference());
}
// update references
String replacement = newName.isEmpty ? '' : '.$newName';
for (SourceReference reference in references) {
reference.addEdit(change, replacement);
}
}
void _analyzePossibleConflicts(RefactoringStatus result) {
ClassElement parentClass = element.enclosingElement;
// Check if the "newName" is the name of the enclosing class.
if (parentClass.name == newName) {
result.addError('The constructor should not have the same name '
'as the name of the enclosing class.');
}
// check if there are members with "newName" in the same ClassElement
for (Element newNameMember in getChildren(parentClass, newName)) {
String message = format(
"Class '{0}' already declares {1} with name '{2}'.",
parentClass.displayName,
getElementKindName(newNameMember),
newName);
result.addError(message, newLocation_fromElement(newNameMember));
}
}
SourceReference _createDeclarationReference() {
SourceRange sourceRange;
int offset = element.periodOffset;
if (offset != null) {
sourceRange = range.startOffsetEndOffset(offset, element.nameEnd);
} else {
sourceRange = new SourceRange(element.nameEnd, 0);
}
return new SourceReference(new SearchMatchImpl(
element.source.fullName,
element.library.source,
element.source,
element.library,
element,
true,
true,
MatchKind.DECLARATION,
sourceRange));
}
Future<void> _replaceSynthetic() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
ClassElement classElement = element.enclosingElement;
AstNode name = await astProvider.getResolvedNameForElement(classElement);
ClassDeclaration classNode = name.parent as ClassDeclaration;
CorrectionUtils utils = new CorrectionUtils(classNode.parent);
ClassMemberLocation location =
utils.prepareNewConstructorLocation(classNode);
doSourceChange_addElementEdit(
change,
classElement,
new SourceEdit(
location.offset,
0,
location.prefix +
'${classElement.name}.$newName();' +
location.suffix));
}
}