blob: 5aaf8adfe0ac581afda519b3c91f16a859fa24c6 [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 'package:analysis_server/src/protocol_server.dart' hide Element;
import 'package:analysis_server/src/services/correction/status.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/search/hierarchy.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/analysis/session_helper.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
/// [ConvertMethodToGetterRefactoring] implementation.
class ConvertGetterToMethodRefactoringImpl extends RefactoringImpl
implements ConvertGetterToMethodRefactoring {
final SearchEngine searchEngine;
final AnalysisSession session;
final PropertyAccessorElement element;
late SourceChange change;
ConvertGetterToMethodRefactoringImpl(
this.searchEngine, this.session, this.element);
@override
String get refactoringName => 'Convert Getter To Method';
@override
Future<RefactoringStatus> checkFinalConditions() {
var result = RefactoringStatus();
return Future.value(result);
}
@override
Future<RefactoringStatus> checkInitialConditions() {
var result = _checkInitialConditions();
return Future.value(result);
}
@override
Future<SourceChange> createChange() async {
change = SourceChange(refactoringName);
// function
if (element.enclosingElement is CompilationUnitElement) {
await _updateElementDeclaration(element);
await _updateElementReferences(element);
}
// method
var field = element.variable;
if (field is FieldElement &&
(field.enclosingElement is ClassElement ||
field.enclosingElement is ExtensionElement)) {
var elements = await getHierarchyMembers(searchEngine, field);
await Future.forEach(elements, (ClassMemberElement member) async {
if (member is FieldElement) {
var getter = member.getter;
if (getter != null && !getter.isSynthetic) {
await _updateElementDeclaration(getter);
return _updateElementReferences(getter);
}
}
});
}
// done
return change;
}
RefactoringStatus _checkInitialConditions() {
if (!element.isGetter || element.isSynthetic) {
return RefactoringStatus.fatal(
'Only explicit getters can be converted to methods.');
}
return RefactoringStatus();
}
Future<void> _updateElementDeclaration(
PropertyAccessorElement element) async {
// prepare "get" keyword
Token? getKeyword;
{
var sessionHelper = AnalysisSessionHelper(session);
var result = await sessionHelper.getElementDeclaration(element);
var declaration = result?.node;
if (declaration is MethodDeclaration) {
getKeyword = declaration.propertyKeyword;
} else if (declaration is FunctionDeclaration) {
getKeyword = declaration.propertyKeyword;
} else {
return;
}
}
// remove "get "
if (getKeyword != null) {
var getRange =
range.startOffsetEndOffset(getKeyword.offset, element.nameOffset);
var edit = newSourceEdit_range(getRange, '');
doSourceChange_addElementEdit(change, element, edit);
}
// add parameters "()"
{
var edit = SourceEdit(range.elementName(element).end, 0, '()');
doSourceChange_addElementEdit(change, element, edit);
}
}
Future _updateElementReferences(Element element) async {
var matches = await searchEngine.searchReferences(element);
var references = getSourceReferences(matches);
for (var reference in references) {
var refElement = reference.element;
var refRange = reference.range;
// insert "()"
var edit = SourceEdit(refRange.end, 0, '()');
doSourceChange_addElementEdit(change, refElement, edit);
}
}
}