Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1449)

Unified Diff: pkg/analysis_server/lib/src/services/refactoring/inline_local.dart

Issue 515733002: 'Inline Local' refactoring. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_server/lib/src/services/refactoring/inline_local.dart
diff --git a/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart b/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart
new file mode 100644
index 0000000000000000000000000000000000000000..f9aea0ceccb220679d9e2727ad9fafc7a5d0a528
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart
@@ -0,0 +1,166 @@
+// 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.
+
+library services.src.refactoring.inline_local;
+
+import 'dart:async';
+
+import 'package:analysis_server/src/protocol.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/refactoring.dart';
+import 'package:analysis_server/src/services/refactoring/refactoring_internal.dart';
+import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/java_core.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+
+const String _TOKEN_SEPARATOR = "\uFFFF";
+
+
+/**
+ * [InlineLocalRefactoring] implementation.
+ */
+class InlineLocalRefactoringImpl extends RefactoringImpl implements
+ InlineLocalRefactoring {
+ final SearchEngine searchEngine;
+ final CompilationUnit unit;
+ final LocalVariableElement element;
+ String file;
+ CorrectionUtils utils;
+
+ VariableDeclaration _variableNode;
+ List<SearchMatch> _references;
+
+ InlineLocalRefactoringImpl(this.searchEngine, this.unit, this.element) {
+ file = unit.element.source.fullName;
+ utils = new CorrectionUtils(unit);
+ }
+
+ @override
+ String get refactoringName => 'Inline Local Variable';
+
+ @override
+ int get referenceCount {
+ return _references.length;
+ }
+
+ @override
+ Future<RefactoringStatus> checkFinalConditions() {
+ RefactoringStatus result = new RefactoringStatus();
+ return new Future.value(result);
+ }
+
+ @override
+ Future<RefactoringStatus> checkInitialConditions() {
+ RefactoringStatus result = new RefactoringStatus();
+ // prepare variable
+ {
+ AstNode elementNode = utils.findNode(element.nameOffset);
+ _variableNode = elementNode != null ?
+ elementNode.getAncestor((node) => node is VariableDeclaration) :
+ null;
+ }
+ // should be normal variable declaration statement
+ if (_variableNode.parent is! VariableDeclarationList ||
+ _variableNode.parent.parent is! VariableDeclarationStatement ||
+ _variableNode.parent.parent.parent is! Block) {
+ result = new RefactoringStatus.fatal(
+ 'Local variable declared in '
+ 'statement should be selected to activate this refactoring.');
+ return new Future.value(result);
+ }
+ // should have initializer at declaration
+ if (_variableNode.initializer == null) {
+ String message = format(
+ "Local variable '{0}' is not initialized at declaration.",
+ element.displayName);
+ result =
+ new RefactoringStatus.fatal(message, new Location.fromNode(_variableNode));
+ return new Future.value(result);
+ }
+ // prepare references
+ return searchEngine.searchReferences(element).then((references) {
+ this._references = references;
+ // should not have assignments
+ for (SearchMatch reference in _references) {
+ if (reference.kind != MatchKind.READ) {
+ String message = format(
+ "Local variable '{0}' is assigned more than once.",
+ [element.displayName]);
+ return new RefactoringStatus.fatal(
+ message,
+ new Location.fromMatch(reference));
+ }
+ }
+ // done
+ return result;
+ });
+ }
+
+ @override
+ Future<SourceChange> createChange() {
+ SourceChange change = new SourceChange(refactoringName);
+ // remove declaration
+ {
+ Statement declarationStatement =
+ _variableNode.getAncestor((node) => node is VariableDeclarationStatement);
+ SourceRange range = utils.getLinesRangeStatements([declarationStatement]);
+ change.addEdit(file, new SourceEdit.range(range, ''));
+ }
+ // prepare initializer
+ Expression initializer = _variableNode.initializer;
+ String initializerSource = utils.getNodeText(initializer);
+ int initializerPrecedence = getExpressionPrecedence(initializer);
+ // replace references
+ for (SearchMatch reference in _references) {
+ SourceRange range = reference.sourceRange;
+ String sourceForReference =
+ _getSourceForReference(range, initializerSource, initializerPrecedence);
+ change.addEdit(file, new SourceEdit.range(range, sourceForReference));
+ }
+ // done
+ return new Future.value(change);
+ }
+
+ @override
+ bool requiresPreview() => false;
+
+ /**
+ * Returns the source which should be used to replace the reference with the
+ * given [SourceRange].
+ *
+ * [range] - the [SourceRange] of the reference.
+ * [source] - the source of the initializer, to be inserted at [range].
+ * [precedence] - the precedence of the initializer [source].
+ */
+ String _getSourceForReference(SourceRange range, String source,
+ int precedence) {
+ int offset = range.offset;
+ AstNode node = utils.findNode(offset);
+ AstNode parent = node.parent;
+ if (_isIdentifierStringInterpolation(parent)) {
+ return '{${source}}';
+ }
+ if (precedence < getExpressionParentPrecedence(node)) {
+ return '(${source})';
+ }
+ return source;
+ }
+
+ /**
+ * Checks if the given node is a string interpolation in form `$name`.
+ */
+ bool _isIdentifierStringInterpolation(AstNode parent) {
+ if (parent is InterpolationExpression) {
+ InterpolationExpression element = parent;
+ return element.beginToken.type ==
+ TokenType.STRING_INTERPOLATION_IDENTIFIER;
+ }
+ return false;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698