mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
Remove unintentional dependency on analysis_server
R=scheglov@google.com Review-Url: https://codereview.chromium.org/2875323002 .
This commit is contained in:
parent
25e8941cc7
commit
bef53e72e4
18 changed files with 556 additions and 145 deletions
|
@ -46,6 +46,7 @@ import 'package:analyzer/src/generated/java_core.dart';
|
|||
import 'package:analyzer/src/generated/parser.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/generated/utilities_dart.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
|
||||
import 'package:analyzer_plugin/utilities/range_factory.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
// 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.correction.name_suggestion;
|
||||
|
||||
import 'package:analysis_server/src/services/correction/strings.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
|
||||
|
||||
List<String> _KNOWN_METHOD_NAME_PREFIXES = ['get', 'is', 'to'];
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
// 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.correction.strings;
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
|
||||
|
||||
/**
|
||||
* "$"
|
||||
*/
|
||||
|
@ -158,46 +158,6 @@ int findCommonSuffix(String a, String b) {
|
|||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of words for the given camel case string.
|
||||
*
|
||||
* 'getCamelWords' => ['get', 'Camel', 'Words']
|
||||
* 'getHTMLText' => ['get', 'HTML', 'Text']
|
||||
*/
|
||||
List<String> getCamelWords(String str) {
|
||||
if (str == null || str.isEmpty) {
|
||||
return <String>[];
|
||||
}
|
||||
List<String> parts = <String>[];
|
||||
bool wasLowerCase = false;
|
||||
bool wasUpperCase = false;
|
||||
int wordStart = 0;
|
||||
for (int i = 0; i < str.length; i++) {
|
||||
int c = str.codeUnitAt(i);
|
||||
var newLowerCase = isLowerCase(c);
|
||||
var newUpperCase = isUpperCase(c);
|
||||
// myWord
|
||||
// | ^
|
||||
if (wasLowerCase && newUpperCase) {
|
||||
parts.add(str.substring(wordStart, i));
|
||||
wordStart = i;
|
||||
}
|
||||
// myHTMLText
|
||||
// | ^
|
||||
if (wasUpperCase &&
|
||||
newUpperCase &&
|
||||
i + 1 < str.length &&
|
||||
isLowerCase(str.codeUnitAt(i + 1))) {
|
||||
parts.add(str.substring(wordStart, i));
|
||||
wordStart = i;
|
||||
}
|
||||
wasLowerCase = newLowerCase;
|
||||
wasUpperCase = newUpperCase;
|
||||
}
|
||||
parts.add(str.substring(wordStart));
|
||||
return parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if [str] is `null`, empty or is whitespace.
|
||||
*/
|
||||
|
@ -215,10 +175,6 @@ bool isDigit(int c) {
|
|||
return c >= 0x30 && c <= 0x39;
|
||||
}
|
||||
|
||||
bool isEmpty(String str) {
|
||||
return str == null || str.isEmpty;
|
||||
}
|
||||
|
||||
bool isEOL(int c) {
|
||||
return c == 0x0D || c == 0x0A;
|
||||
}
|
||||
|
@ -231,16 +187,8 @@ bool isLetterOrDigit(int c) {
|
|||
return isLetter(c) || isDigit(c);
|
||||
}
|
||||
|
||||
bool isLowerCase(int c) {
|
||||
return c >= 0x61 && c <= 0x7A;
|
||||
}
|
||||
|
||||
bool isSpace(int c) => c == 0x20 || c == 0x09;
|
||||
|
||||
bool isUpperCase(int c) {
|
||||
return c >= 0x41 && c <= 0x5A;
|
||||
}
|
||||
|
||||
bool isWhitespace(int c) {
|
||||
return isSpace(c) || isEOL(c);
|
||||
}
|
||||
|
@ -262,16 +210,6 @@ String removeEnd(String str, String remove) {
|
|||
return str;
|
||||
}
|
||||
|
||||
String removeStart(String str, String remove) {
|
||||
if (isEmpty(str) || isEmpty(remove)) {
|
||||
return str;
|
||||
}
|
||||
if (str.startsWith(remove)) {
|
||||
return str.substring(remove.length);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
String repeat(String s, int n) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// 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.correction.util;
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:analysis_server/protocol/protocol_generated.dart'
|
||||
|
@ -22,6 +20,7 @@ import 'package:analyzer/src/dart/scanner/scanner.dart';
|
|||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
|
||||
import 'package:analyzer_plugin/utilities/range_factory.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
// 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.naming_conventions;
|
||||
|
||||
import 'package:analysis_server/src/services/correction/status.dart';
|
||||
import 'package:analysis_server/src/services/correction/strings.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
|
||||
import 'package:front_end/src/scanner/token.dart' show Keyword;
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analysis_server/src/services/correction/strings.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// 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 test.services.correction.strings;
|
||||
|
||||
import 'package:analysis_server/src/services/correction/strings.dart';
|
||||
import 'package:test/test.dart' hide isEmpty;
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
@ -75,13 +73,6 @@ class StringsTest {
|
|||
expect(findCommonSuffix('123', 'xyz123'), 3);
|
||||
}
|
||||
|
||||
void test_getCamelWords() {
|
||||
expect(getCamelWords(null), []);
|
||||
expect(getCamelWords(''), []);
|
||||
expect(getCamelWords('getCamelWords'), ['get', 'Camel', 'Words']);
|
||||
expect(getCamelWords('getHTMLText'), ['get', 'HTML', 'Text']);
|
||||
}
|
||||
|
||||
void test_isBlank() {
|
||||
expect(isBlank(null), isTrue);
|
||||
expect(isBlank(''), isTrue);
|
||||
|
@ -99,13 +90,6 @@ class StringsTest {
|
|||
expect(isDigit('A'.codeUnitAt(0)), isFalse);
|
||||
}
|
||||
|
||||
void test_isEmpty() {
|
||||
expect(isEmpty(null), isTrue);
|
||||
expect(isEmpty(''), isTrue);
|
||||
expect(isEmpty('X'), isFalse);
|
||||
expect(isEmpty(' '), isFalse);
|
||||
}
|
||||
|
||||
void test_isLetter() {
|
||||
for (int c in 'abcdefghijklmnopqrstuvwxyz'.codeUnits) {
|
||||
expect(isLetter(c), isTrue);
|
||||
|
@ -131,17 +115,6 @@ class StringsTest {
|
|||
expect(isLetterOrDigit('.'.codeUnitAt(0)), isFalse);
|
||||
}
|
||||
|
||||
void test_isLowerCase() {
|
||||
for (int c in 'abcdefghijklmnopqrstuvwxyz'.codeUnits) {
|
||||
expect(isLowerCase(c), isTrue);
|
||||
}
|
||||
for (int c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.codeUnits) {
|
||||
expect(isLowerCase(c), isFalse);
|
||||
}
|
||||
expect(isLowerCase(' '.codeUnitAt(0)), isFalse);
|
||||
expect(isLowerCase('0'.codeUnitAt(0)), isFalse);
|
||||
}
|
||||
|
||||
void test_isSpace() {
|
||||
expect(isSpace(' '.codeUnitAt(0)), isTrue);
|
||||
expect(isSpace('\t'.codeUnitAt(0)), isTrue);
|
||||
|
@ -151,17 +124,6 @@ class StringsTest {
|
|||
expect(isSpace('A'.codeUnitAt(0)), isFalse);
|
||||
}
|
||||
|
||||
void test_isUpperCase() {
|
||||
for (int c in 'abcdefghijklmnopqrstuvwxyz'.codeUnits) {
|
||||
expect(isUpperCase(c), isFalse);
|
||||
}
|
||||
for (int c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.codeUnits) {
|
||||
expect(isUpperCase(c), isTrue);
|
||||
}
|
||||
expect(isUpperCase(' '.codeUnitAt(0)), isFalse);
|
||||
expect(isUpperCase('0'.codeUnitAt(0)), isFalse);
|
||||
}
|
||||
|
||||
void test_isWhitespace() {
|
||||
expect(isWhitespace(' '.codeUnitAt(0)), isTrue);
|
||||
expect(isWhitespace('\t'.codeUnitAt(0)), isTrue);
|
||||
|
@ -186,13 +148,6 @@ class StringsTest {
|
|||
expect(removeEnd('www.domain.com', '.com'), 'www.domain');
|
||||
}
|
||||
|
||||
void test_removeStart() {
|
||||
expect(removeStart(null, 'x'), null);
|
||||
expect(removeStart('abc', null), 'abc');
|
||||
expect(removeStart('abcTest', 'abc'), 'Test');
|
||||
expect(removeStart('my abcTest', 'abc'), 'my abcTest');
|
||||
}
|
||||
|
||||
void test_repeat() {
|
||||
expect(repeat('x', 0), '');
|
||||
expect(repeat('x', 5), 'xxxxx');
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analysis_server/protocol/protocol_generated.dart';
|
||||
import 'package:analysis_server/src/services/correction/strings.dart';
|
||||
import 'package:analysis_server/src/services/correction/util.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
|
|
|
@ -292,6 +292,14 @@ class LineInfo {
|
|||
}
|
||||
return lineStarts[lineNumber];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the offset of the first character on the line following the line
|
||||
* containing the given [offset].
|
||||
*/
|
||||
int getOffsetOfLineAfter(int offset) {
|
||||
return getOffsetOfLine(getLocation(offset).lineNumber + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analysis_server/protocol/protocol_generated.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analysis_server/protocol/protocol_generated.dart'
|
||||
hide Element, ElementKind;
|
||||
import 'package:analysis_server/src/services/correction/name_suggestion.dart';
|
||||
import 'package:analysis_server/src/services/correction/util.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/token.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
|
@ -17,10 +13,15 @@ import 'package:analyzer/src/dart/ast/utilities.dart';
|
|||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/generated/utilities_dart.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_generated.dart'
|
||||
hide Element, ElementKind;
|
||||
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
|
||||
import 'package:analyzer_plugin/utilities/range_factory.dart';
|
||||
import 'package:charcode/ascii.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
/**
|
||||
* A [ChangeBuilder] used to build changes in Dart files.
|
||||
|
@ -49,6 +50,8 @@ class DartChangeBuilderImpl extends ChangeBuilderImpl
|
|||
* An [EditBuilder] used to build edits in Dart files.
|
||||
*/
|
||||
class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
|
||||
List<String> _KNOWN_METHOD_NAME_PREFIXES = ['get', 'is', 'to'];
|
||||
|
||||
/**
|
||||
* Initialize a newly created builder to build a source edit.
|
||||
*/
|
||||
|
@ -543,6 +546,45 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds [toAdd] items which are not excluded.
|
||||
*/
|
||||
void _addAll(
|
||||
Set<String> excluded, Set<String> result, Iterable<String> toAdd) {
|
||||
for (String item in toAdd) {
|
||||
// add name based on "item", but not "excluded"
|
||||
for (int suffix = 1;; suffix++) {
|
||||
// prepare name, just "item" or "item2", "item3", etc
|
||||
String name = item;
|
||||
if (suffix > 1) {
|
||||
name += suffix.toString();
|
||||
}
|
||||
// add once found not excluded
|
||||
if (!excluded.contains(name)) {
|
||||
result.add(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds to [result] either [c] or the first ASCII character after it.
|
||||
*/
|
||||
void _addSingleCharacterName(
|
||||
Set<String> excluded, Set<String> result, int c) {
|
||||
while (c < $z) {
|
||||
String name = new String.fromCharCode(c);
|
||||
// may be done
|
||||
if (!excluded.contains(name)) {
|
||||
result.add(name);
|
||||
break;
|
||||
}
|
||||
// next character
|
||||
c = c + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void _addSuperTypeProposals(
|
||||
LinkedEditBuilder builder, DartType type, Set<DartType> alreadyAdded) {
|
||||
if (type != null &&
|
||||
|
@ -557,21 +599,124 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
String _getBaseNameFromExpression(Expression expression) {
|
||||
String name = null;
|
||||
// e as Type
|
||||
if (expression is AsExpression) {
|
||||
AsExpression asExpression = expression as AsExpression;
|
||||
expression = asExpression.expression;
|
||||
}
|
||||
// analyze expressions
|
||||
if (expression is SimpleIdentifier) {
|
||||
SimpleIdentifier node = expression;
|
||||
return node.name;
|
||||
} else if (expression is PrefixedIdentifier) {
|
||||
PrefixedIdentifier node = expression;
|
||||
return node.identifier.name;
|
||||
} else if (expression is PropertyAccess) {
|
||||
PropertyAccess node = expression;
|
||||
return node.propertyName.name;
|
||||
} else if (expression is MethodInvocation) {
|
||||
name = expression.methodName.name;
|
||||
} else if (expression is InstanceCreationExpression) {
|
||||
InstanceCreationExpression creation = expression;
|
||||
ConstructorName constructorName = creation.constructorName;
|
||||
TypeName typeName = constructorName.type;
|
||||
if (typeName != null) {
|
||||
Identifier typeNameIdentifier = typeName.name;
|
||||
// new ClassName()
|
||||
if (typeNameIdentifier is SimpleIdentifier) {
|
||||
return typeNameIdentifier.name;
|
||||
}
|
||||
// new prefix.name();
|
||||
if (typeNameIdentifier is PrefixedIdentifier) {
|
||||
PrefixedIdentifier prefixed = typeNameIdentifier;
|
||||
// new prefix.ClassName()
|
||||
if (prefixed.prefix.staticElement is PrefixElement) {
|
||||
return prefixed.identifier.name;
|
||||
}
|
||||
// new ClassName.constructorName()
|
||||
return prefixed.prefix.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
// strip known prefixes
|
||||
if (name != null) {
|
||||
for (int i = 0; i < _KNOWN_METHOD_NAME_PREFIXES.length; i++) {
|
||||
String prefix = _KNOWN_METHOD_NAME_PREFIXES[i];
|
||||
if (name.startsWith(prefix)) {
|
||||
if (name == prefix) {
|
||||
return null;
|
||||
} else if (isUpperCase(name.codeUnitAt(prefix.length))) {
|
||||
return name.substring(prefix.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// done
|
||||
return name;
|
||||
}
|
||||
|
||||
String _getBaseNameFromLocationInParent(Expression expression) {
|
||||
// value in named expression
|
||||
if (expression.parent is NamedExpression) {
|
||||
NamedExpression namedExpression = expression.parent as NamedExpression;
|
||||
if (namedExpression.expression == expression) {
|
||||
return namedExpression.name.label.name;
|
||||
}
|
||||
}
|
||||
// positional argument
|
||||
ParameterElement parameter = expression.propagatedParameterElement;
|
||||
if (parameter == null) {
|
||||
parameter = expression.staticParameterElement;
|
||||
}
|
||||
if (parameter != null) {
|
||||
return parameter.displayName;
|
||||
}
|
||||
|
||||
// unknown
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all variants of names by removing leading words one by one.
|
||||
*/
|
||||
List<String> _getCamelWordCombinations(String name) {
|
||||
List<String> result = [];
|
||||
List<String> parts = getCamelWords(name);
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
String s1 = parts[i].toLowerCase();
|
||||
String s2 = parts.skip(i + 1).join();
|
||||
String suggestion = '$s1$s2';
|
||||
result.add(suggestion);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the import element used to import the given [element] into the given
|
||||
* [library], or `null` if the element was not imported, such as when the
|
||||
* element is declared in the same library.
|
||||
*/
|
||||
ImportElement _getImportElement(Element element, LibraryElement library) {
|
||||
for (ImportElement imp in library.imports) {
|
||||
Map<String, Element> definedNames = getImportNamespace(imp);
|
||||
for (ImportElement importElement in library.imports) {
|
||||
Map<String, Element> definedNames = _getImportNamespace(importElement);
|
||||
if (definedNames.containsValue(element)) {
|
||||
return imp;
|
||||
return importElement;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the namespace added by the given import [element].
|
||||
*/
|
||||
Map<String, Element> _getImportNamespace(ImportElement element) {
|
||||
NamespaceBuilder builder = new NamespaceBuilder();
|
||||
Namespace namespace = builder.createImportNamespaceForDirective(element);
|
||||
return namespace.definedNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list containing the suggested names for a parameter with the given
|
||||
* [type] whose value in one location is computed by the given [expression].
|
||||
|
@ -582,7 +727,7 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
|
|||
List<String> _getParameterNameSuggestions(
|
||||
Set<String> usedNames, DartType type, Expression expression, int index) {
|
||||
List<String> suggestions =
|
||||
getVariableNameSuggestionsForExpression(type, expression, usedNames);
|
||||
_getVariableNameSuggestionsForExpression(type, expression, usedNames);
|
||||
if (suggestions.length != 0) {
|
||||
return suggestions;
|
||||
}
|
||||
|
@ -737,6 +882,45 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns possible names for a variable with the given expected type and
|
||||
* expression assigned.
|
||||
*/
|
||||
List<String> _getVariableNameSuggestionsForExpression(DartType expectedType,
|
||||
Expression assignedExpression, Set<String> excluded) {
|
||||
Set<String> res = new Set();
|
||||
// use expression
|
||||
if (assignedExpression != null) {
|
||||
String nameFromExpression =
|
||||
_getBaseNameFromExpression(assignedExpression);
|
||||
if (nameFromExpression != null) {
|
||||
nameFromExpression = removeStart(nameFromExpression, '_');
|
||||
_addAll(excluded, res, _getCamelWordCombinations(nameFromExpression));
|
||||
}
|
||||
String nameFromParent =
|
||||
_getBaseNameFromLocationInParent(assignedExpression);
|
||||
if (nameFromParent != null) {
|
||||
_addAll(excluded, res, _getCamelWordCombinations(nameFromParent));
|
||||
}
|
||||
}
|
||||
// use type
|
||||
if (expectedType != null && !expectedType.isDynamic) {
|
||||
String typeName = expectedType.name;
|
||||
if ('int' == typeName) {
|
||||
_addSingleCharacterName(excluded, res, $i);
|
||||
} else if ('double' == typeName) {
|
||||
_addSingleCharacterName(excluded, res, $d);
|
||||
} else if ('String' == typeName) {
|
||||
_addSingleCharacterName(excluded, res, $s);
|
||||
} else {
|
||||
_addAll(excluded, res, _getCamelWordCombinations(typeName));
|
||||
}
|
||||
res.remove(typeName);
|
||||
}
|
||||
// done
|
||||
return new List.from(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if [type] is visible in either the [enclosingExecutable] or
|
||||
* [enclosingClass].
|
||||
|
@ -806,25 +990,10 @@ class DartFileEditBuilderImpl extends FileEditBuilderImpl
|
|||
|
||||
@override
|
||||
void finalize() {
|
||||
addLibraryImports(
|
||||
_addLibraryImports(
|
||||
changeBuilder.sourceChange, unit.element.library, librariesToImport);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Return the content of the file being edited.
|
||||
// */
|
||||
// String getContent() {
|
||||
// if (_content == null) {
|
||||
// CompilationUnitElement unitElement = unit.element;
|
||||
// AnalysisContext context = unitElement.context;
|
||||
// if (context == null) {
|
||||
// throw new CancelCorrectionException();
|
||||
// }
|
||||
// _content = context.getContents(unitElement.source).data;
|
||||
// }
|
||||
// return _content;
|
||||
// }
|
||||
|
||||
@override
|
||||
void importLibraries(Iterable<Source> libraries) {
|
||||
librariesToImport.addAll(libraries);
|
||||
|
@ -852,6 +1021,203 @@ class DartFileEditBuilderImpl extends FileEditBuilderImpl
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds edits to the given [change] that ensure that all the [libraries] are
|
||||
* imported into the given [targetLibrary].
|
||||
*/
|
||||
void _addLibraryImports(SourceChange change, LibraryElement targetLibrary,
|
||||
Set<Source> libraries) {
|
||||
// Prepare information about existing imports.
|
||||
LibraryDirective libraryDirective;
|
||||
List<ImportDirective> importDirectives = <ImportDirective>[];
|
||||
for (Directive directive in unit.directives) {
|
||||
if (directive is LibraryDirective) {
|
||||
libraryDirective = directive;
|
||||
} else if (directive is ImportDirective) {
|
||||
importDirectives.add(directive);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare all URIs to import.
|
||||
List<String> uriList = libraries
|
||||
.map((library) => _getLibrarySourceUri(targetLibrary, library))
|
||||
.toList();
|
||||
uriList.sort((a, b) => a.compareTo(b));
|
||||
|
||||
// Insert imports: between existing imports.
|
||||
if (importDirectives.isNotEmpty) {
|
||||
bool isFirstPackage = true;
|
||||
for (String importUri in uriList) {
|
||||
bool inserted = false;
|
||||
bool isPackage = importUri.startsWith('package:');
|
||||
bool isAfterDart = false;
|
||||
for (ImportDirective existingImport in importDirectives) {
|
||||
if (existingImport.uriContent.startsWith('dart:')) {
|
||||
isAfterDart = true;
|
||||
}
|
||||
if (existingImport.uriContent.startsWith('package:')) {
|
||||
isFirstPackage = false;
|
||||
}
|
||||
if (importUri.compareTo(existingImport.uriContent) < 0) {
|
||||
addInsertion(existingImport.offset, (EditBuilder builder) {
|
||||
builder.write("import '");
|
||||
builder.write(importUri);
|
||||
builder.writeln("';");
|
||||
});
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inserted) {
|
||||
addInsertion(importDirectives.last.end, (EditBuilder builder) {
|
||||
if (isPackage && isFirstPackage && isAfterDart) {
|
||||
builder.writeln();
|
||||
}
|
||||
builder.writeln();
|
||||
builder.write("import '");
|
||||
builder.write(importUri);
|
||||
builder.write("';");
|
||||
});
|
||||
}
|
||||
if (isPackage) {
|
||||
isFirstPackage = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert imports: after the library directive.
|
||||
if (libraryDirective != null) {
|
||||
for (int i = 0; i < uriList.length; i++) {
|
||||
String importUri = uriList[i];
|
||||
addInsertion(libraryDirective.end, (EditBuilder builder) {
|
||||
if (i == 0) {
|
||||
builder.writeln();
|
||||
}
|
||||
builder.writeln();
|
||||
builder.write("import '");
|
||||
builder.write(importUri);
|
||||
builder.writeln("';");
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If still at the beginning of the file, skip shebang and line comments.
|
||||
_InsertionDescription desc = _getInsertDescTop();
|
||||
int offset = desc.offset;
|
||||
for (int i = 0; i < uriList.length; i++) {
|
||||
String importUri = uriList[i];
|
||||
addInsertion(offset, (EditBuilder builder) {
|
||||
if (i == 0 && desc.insertEmptyLineBefore) {
|
||||
builder.writeln();
|
||||
}
|
||||
builder.write("import '");
|
||||
builder.write(importUri);
|
||||
builder.writeln("';");
|
||||
if (i == uriList.length - 1 && desc.insertEmptyLineAfter) {
|
||||
builder.writeln();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [InsertDesc] describing where to insert a new directive or a
|
||||
* top-level declaration at the top of the file.
|
||||
*/
|
||||
_InsertionDescription _getInsertDescTop() {
|
||||
// skip leading line comments
|
||||
int offset = 0;
|
||||
bool insertEmptyLineBefore = false;
|
||||
bool insertEmptyLineAfter = false;
|
||||
String source = unit.element.context.getContents(unit.element.source).data;
|
||||
var lineInfo = unit.lineInfo;
|
||||
// skip hash-bang
|
||||
if (offset < source.length - 2) {
|
||||
String linePrefix = _getText(source, offset, 2);
|
||||
if (linePrefix == "#!") {
|
||||
insertEmptyLineBefore = true;
|
||||
offset = lineInfo.getOffsetOfLineAfter(offset);
|
||||
// skip empty lines to first line comment
|
||||
int emptyOffset = offset;
|
||||
while (emptyOffset < source.length - 2) {
|
||||
int nextLineOffset = lineInfo.getOffsetOfLineAfter(emptyOffset);
|
||||
String line = source.substring(emptyOffset, nextLineOffset);
|
||||
if (line.trim().isEmpty) {
|
||||
emptyOffset = nextLineOffset;
|
||||
continue;
|
||||
} else if (line.startsWith("//")) {
|
||||
offset = emptyOffset;
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// skip line comments
|
||||
while (offset < source.length - 2) {
|
||||
String linePrefix = _getText(source, offset, 2);
|
||||
if (linePrefix == "//") {
|
||||
insertEmptyLineBefore = true;
|
||||
offset = lineInfo.getOffsetOfLineAfter(offset);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// determine if empty line is required after
|
||||
int currentLine = lineInfo.getLocation(offset).lineNumber;
|
||||
if (currentLine < lineInfo.lineCount) {
|
||||
int nextLineOffset = lineInfo.getOffsetOfLine(currentLine + 1);
|
||||
String insertLine = source.substring(offset, nextLineOffset);
|
||||
if (!insertLine.trim().isEmpty) {
|
||||
insertEmptyLineAfter = true;
|
||||
}
|
||||
}
|
||||
return new _InsertionDescription(
|
||||
offset, insertEmptyLineBefore, insertEmptyLineAfter);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Return the content of the file being edited.
|
||||
// */
|
||||
// String getContent() {
|
||||
// if (_content == null) {
|
||||
// CompilationUnitElement unitElement = unit.element;
|
||||
// AnalysisContext context = unitElement.context;
|
||||
// if (context == null) {
|
||||
// throw new CancelCorrectionException();
|
||||
// }
|
||||
// _content = context.getContents(unitElement.source).data;
|
||||
// }
|
||||
// return _content;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Computes the best URI to import [what] into [from].
|
||||
*/
|
||||
String _getLibrarySourceUri(LibraryElement from, Source what) {
|
||||
String whatPath = what.fullName;
|
||||
// check if an absolute URI (such as 'dart:' or 'package:')
|
||||
Uri whatUri = what.uri;
|
||||
String whatUriScheme = whatUri.scheme;
|
||||
if (whatUriScheme != '' && whatUriScheme != 'file') {
|
||||
return whatUri.toString();
|
||||
}
|
||||
// compute a relative URI
|
||||
String fromFolder = path.dirname(from.source.fullName);
|
||||
String relativeFile = path.relative(whatPath, from: fromFolder);
|
||||
return path.split(relativeFile).join('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text of the given range in the unit.
|
||||
*/
|
||||
String _getText(String content, int offset, int length) {
|
||||
return content.substring(offset, offset + length);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Returns the text of the given [AstNode] in the unit.
|
||||
// */
|
||||
|
@ -940,3 +1306,11 @@ class _EnclosingElementFinder {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _InsertionDescription {
|
||||
final int offset;
|
||||
final bool insertEmptyLineBefore;
|
||||
final bool insertEmptyLineAfter;
|
||||
_InsertionDescription(
|
||||
this.offset, this.insertEmptyLineBefore, this.insertEmptyLineAfter);
|
||||
}
|
||||
|
|
76
pkg/analyzer_plugin/lib/src/utilities/string_utilities.dart
Normal file
76
pkg/analyzer_plugin/lib/src/utilities/string_utilities.dart
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) 2017, 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:charcode/ascii.dart';
|
||||
|
||||
/**
|
||||
* Returns a list of the words from which the given camel case [string] is
|
||||
* composed.
|
||||
*
|
||||
* 'getCamelWords' => ['get', 'Camel', 'Words']
|
||||
* 'getHTMLText' => ['get', 'HTML', 'Text']
|
||||
*/
|
||||
List<String> getCamelWords(String string) {
|
||||
if (string == null || string.isEmpty) {
|
||||
return const <String>[];
|
||||
}
|
||||
List<String> parts = <String>[];
|
||||
bool wasLowerCase = false;
|
||||
bool wasUpperCase = false;
|
||||
int wordStart = 0;
|
||||
for (int i = 0; i < string.length; i++) {
|
||||
int c = string.codeUnitAt(i);
|
||||
var newLowerCase = isLowerCase(c);
|
||||
var newUpperCase = isUpperCase(c);
|
||||
// myWord
|
||||
// | ^
|
||||
if (wasLowerCase && newUpperCase) {
|
||||
parts.add(string.substring(wordStart, i));
|
||||
wordStart = i;
|
||||
}
|
||||
// myHTMLText
|
||||
// | ^
|
||||
if (wasUpperCase &&
|
||||
newUpperCase &&
|
||||
i + 1 < string.length &&
|
||||
isLowerCase(string.codeUnitAt(i + 1))) {
|
||||
parts.add(string.substring(wordStart, i));
|
||||
wordStart = i;
|
||||
}
|
||||
wasLowerCase = newLowerCase;
|
||||
wasUpperCase = newUpperCase;
|
||||
}
|
||||
parts.add(string.substring(wordStart));
|
||||
return parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the given [string] is either `null` or empty.
|
||||
*/
|
||||
bool isEmpty(String string) => string == null || string.isEmpty;
|
||||
|
||||
/**
|
||||
* Return `true` if the given [character] is a lowercase ASCII character.
|
||||
*/
|
||||
bool isLowerCase(int character) => character >= $a && character <= $z;
|
||||
|
||||
/**
|
||||
* Return `true` if the given [character] is an uppercase ASCII character.
|
||||
*/
|
||||
bool isUpperCase(int character) => character >= $A && character <= $Z;
|
||||
|
||||
/**
|
||||
* If the given [string] starts with the text to [remove], then return the
|
||||
* portion of the string after the text to remove. Otherwise, return the
|
||||
* original string unmodified.
|
||||
*/
|
||||
String removeStart(String string, String remove) {
|
||||
if (isEmpty(string) || isEmpty(remove)) {
|
||||
return string;
|
||||
}
|
||||
if (string.startsWith(remove)) {
|
||||
return string.substring(remove.length);
|
||||
}
|
||||
return string;
|
||||
}
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analysis_server/protocol/protocol_generated.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ environment:
|
|||
|
||||
dependencies:
|
||||
analyzer: ^0.30.0
|
||||
charcode: ^1.1.0
|
||||
html: any
|
||||
path: any
|
||||
pub_semver: any
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// 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/protocol/protocol_generated.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analysis_server/protocol/protocol_generated.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/standard_resolution_map.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
|
@ -15,6 +14,7 @@ import 'package:analyzer/src/generated/engine.dart';
|
|||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/generated/testing/test_type_provider.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
|
||||
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_dart.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2017, 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/src/utilities/string_utilities.dart';
|
||||
import 'package:test/test.dart' hide isEmpty;
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(StringUtilitiesTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class StringUtilitiesTest {
|
||||
void test_getCamelWords() {
|
||||
expect(getCamelWords(null), []);
|
||||
expect(getCamelWords(''), []);
|
||||
expect(getCamelWords('getCamelWords'), ['get', 'Camel', 'Words']);
|
||||
expect(getCamelWords('getHTMLText'), ['get', 'HTML', 'Text']);
|
||||
}
|
||||
|
||||
void test_isEmpty() {
|
||||
expect(isEmpty(null), isTrue);
|
||||
expect(isEmpty(''), isTrue);
|
||||
expect(isEmpty('X'), isFalse);
|
||||
expect(isEmpty(' '), isFalse);
|
||||
}
|
||||
|
||||
void test_isLowerCase() {
|
||||
for (int c in 'abcdefghijklmnopqrstuvwxyz'.codeUnits) {
|
||||
expect(isLowerCase(c), isTrue);
|
||||
}
|
||||
for (int c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.codeUnits) {
|
||||
expect(isLowerCase(c), isFalse);
|
||||
}
|
||||
expect(isLowerCase(' '.codeUnitAt(0)), isFalse);
|
||||
expect(isLowerCase('0'.codeUnitAt(0)), isFalse);
|
||||
}
|
||||
|
||||
void test_isUpperCase() {
|
||||
for (int c in 'abcdefghijklmnopqrstuvwxyz'.codeUnits) {
|
||||
expect(isUpperCase(c), isFalse);
|
||||
}
|
||||
for (int c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.codeUnits) {
|
||||
expect(isUpperCase(c), isTrue);
|
||||
}
|
||||
expect(isUpperCase(' '.codeUnitAt(0)), isFalse);
|
||||
expect(isUpperCase('0'.codeUnitAt(0)), isFalse);
|
||||
}
|
||||
|
||||
void test_removeStart() {
|
||||
expect(removeStart(null, 'x'), null);
|
||||
expect(removeStart('abc', null), 'abc');
|
||||
expect(removeStart('abcTest', 'abc'), 'Test');
|
||||
expect(removeStart('my abcTest', 'abc'), 'my abcTest');
|
||||
}
|
||||
}
|
|
@ -5,9 +5,11 @@
|
|||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'change_builder/test_all.dart' as change_builder;
|
||||
import 'string_utilities_test.dart' as string_utilities;
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
change_builder.main();
|
||||
string_utilities.main();
|
||||
}, name: 'utilities');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue