ed26cdbf05
R=brianwilkerson@google.com BUG= Review URL: https://codereview.chromium.org//1070223002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@45025 260f80e4-7a28-3924-810f-c04153c831b5
420 lines
13 KiB
Dart
420 lines
13 KiB
Dart
// 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 protocol.server;
|
|
|
|
import 'package:analysis_server/src/protocol.dart';
|
|
import 'package:analysis_server/src/services/search/search_engine.dart'
|
|
as engine;
|
|
import 'package:analyzer/src/generated/ast.dart' as engine;
|
|
import 'package:analyzer/src/generated/element.dart' as engine;
|
|
import 'package:analyzer/src/generated/engine.dart' as engine;
|
|
import 'package:analyzer/src/generated/error.dart' as engine;
|
|
import 'package:analyzer/src/generated/source.dart' as engine;
|
|
import 'package:analyzer/src/generated/utilities_dart.dart' as engine;
|
|
|
|
export 'package:analysis_server/src/protocol.dart';
|
|
|
|
/**
|
|
* Returns a list of AnalysisErrors correponding to the given list of Engine
|
|
* errors.
|
|
*/
|
|
List<AnalysisError> doAnalysisError_listFromEngine(
|
|
engine.LineInfo lineInfo, List<engine.AnalysisError> errors) {
|
|
return errors.map((engine.AnalysisError error) {
|
|
return newAnalysisError_fromEngine(lineInfo, error);
|
|
}).toList();
|
|
}
|
|
|
|
/**
|
|
* Adds [edit] to the [FileEdit] for the given [element].
|
|
*/
|
|
void doSourceChange_addElementEdit(
|
|
SourceChange change, engine.Element element, SourceEdit edit) {
|
|
engine.AnalysisContext context = element.context;
|
|
engine.Source source = element.source;
|
|
doSourceChange_addSourceEdit(change, context, source, edit);
|
|
}
|
|
|
|
/**
|
|
* Adds [edit] to the [FileEdit] for the given [source].
|
|
*/
|
|
void doSourceChange_addSourceEdit(SourceChange change,
|
|
engine.AnalysisContext context, engine.Source source, SourceEdit edit) {
|
|
String file = source.fullName;
|
|
int fileStamp = context.getModificationStamp(source);
|
|
change.addEdit(file, fileStamp, edit);
|
|
}
|
|
|
|
/**
|
|
* Construct based on error information from the analyzer engine.
|
|
*/
|
|
AnalysisError newAnalysisError_fromEngine(
|
|
engine.LineInfo lineInfo, engine.AnalysisError error) {
|
|
engine.ErrorCode errorCode = error.errorCode;
|
|
// prepare location
|
|
Location location;
|
|
{
|
|
String file = error.source.fullName;
|
|
int offset = error.offset;
|
|
int length = error.length;
|
|
int startLine = -1;
|
|
int startColumn = -1;
|
|
if (lineInfo != null) {
|
|
engine.LineInfo_Location lineLocation = lineInfo.getLocation(offset);
|
|
if (lineLocation != null) {
|
|
startLine = lineLocation.lineNumber;
|
|
startColumn = lineLocation.columnNumber;
|
|
}
|
|
}
|
|
location = new Location(file, offset, length, startLine, startColumn);
|
|
}
|
|
// done
|
|
var severity = new AnalysisErrorSeverity(errorCode.errorSeverity.name);
|
|
var type = new AnalysisErrorType(errorCode.type.name);
|
|
String message = error.message;
|
|
String correction = error.correction;
|
|
return new AnalysisError(severity, type, location, message,
|
|
correction: correction);
|
|
}
|
|
|
|
/**
|
|
* Construct based on a value from the analyzer engine.
|
|
*/
|
|
Element newElement_fromEngine(engine.Element element) {
|
|
String name = element.displayName;
|
|
String elementTypeParameters = _getTypeParametersString(element);
|
|
String elementParameters = _getParametersString(element);
|
|
String elementReturnType = _getReturnTypeString(element);
|
|
return new Element(newElementKind_fromEngine(element.kind), name, Element
|
|
.makeFlags(
|
|
isPrivate: element.isPrivate,
|
|
isDeprecated: element.isDeprecated,
|
|
isAbstract: _isAbstract(element),
|
|
isConst: _isConst(element),
|
|
isFinal: _isFinal(element),
|
|
isStatic: _isStatic(element)),
|
|
location: newLocation_fromElement(element),
|
|
typeParameters: elementTypeParameters,
|
|
parameters: elementParameters,
|
|
returnType: elementReturnType);
|
|
}
|
|
|
|
/**
|
|
* Construct based on a value from the analyzer engine.
|
|
*/
|
|
ElementKind newElementKind_fromEngine(engine.ElementKind kind) {
|
|
if (kind == engine.ElementKind.CLASS) {
|
|
return ElementKind.CLASS;
|
|
}
|
|
if (kind == engine.ElementKind.COMPILATION_UNIT) {
|
|
return ElementKind.COMPILATION_UNIT;
|
|
}
|
|
if (kind == engine.ElementKind.CONSTRUCTOR) {
|
|
return ElementKind.CONSTRUCTOR;
|
|
}
|
|
if (kind == engine.ElementKind.FIELD) {
|
|
return ElementKind.FIELD;
|
|
}
|
|
if (kind == engine.ElementKind.FUNCTION) {
|
|
return ElementKind.FUNCTION;
|
|
}
|
|
if (kind == engine.ElementKind.FUNCTION_TYPE_ALIAS) {
|
|
return ElementKind.FUNCTION_TYPE_ALIAS;
|
|
}
|
|
if (kind == engine.ElementKind.GETTER) {
|
|
return ElementKind.GETTER;
|
|
}
|
|
if (kind == engine.ElementKind.LABEL) {
|
|
return ElementKind.LABEL;
|
|
}
|
|
if (kind == engine.ElementKind.LIBRARY) {
|
|
return ElementKind.LIBRARY;
|
|
}
|
|
if (kind == engine.ElementKind.LOCAL_VARIABLE) {
|
|
return ElementKind.LOCAL_VARIABLE;
|
|
}
|
|
if (kind == engine.ElementKind.METHOD) {
|
|
return ElementKind.METHOD;
|
|
}
|
|
if (kind == engine.ElementKind.PARAMETER) {
|
|
return ElementKind.PARAMETER;
|
|
}
|
|
if (kind == engine.ElementKind.PREFIX) {
|
|
return ElementKind.PREFIX;
|
|
}
|
|
if (kind == engine.ElementKind.SETTER) {
|
|
return ElementKind.SETTER;
|
|
}
|
|
if (kind == engine.ElementKind.TOP_LEVEL_VARIABLE) {
|
|
return ElementKind.TOP_LEVEL_VARIABLE;
|
|
}
|
|
if (kind == engine.ElementKind.TYPE_PARAMETER) {
|
|
return ElementKind.TYPE_PARAMETER;
|
|
}
|
|
return ElementKind.UNKNOWN;
|
|
}
|
|
|
|
/**
|
|
* Create a Location based on an [engine.Element].
|
|
*/
|
|
Location newLocation_fromElement(engine.Element element) {
|
|
engine.AnalysisContext context = element.context;
|
|
engine.Source source = element.source;
|
|
if (context == null || source == null) {
|
|
return null;
|
|
}
|
|
String name = element.displayName;
|
|
int offset = element.nameOffset;
|
|
int length = name != null ? name.length : 0;
|
|
if (element is engine.CompilationUnitElement) {
|
|
offset = 0;
|
|
length = 0;
|
|
}
|
|
engine.SourceRange range = new engine.SourceRange(offset, length);
|
|
return _locationForArgs(context, source, range);
|
|
}
|
|
|
|
/**
|
|
* Create a Location based on an [engine.SearchMatch].
|
|
*/
|
|
Location newLocation_fromMatch(engine.SearchMatch match) {
|
|
engine.Element enclosingElement = match.element;
|
|
return _locationForArgs(
|
|
enclosingElement.context, enclosingElement.source, match.sourceRange);
|
|
}
|
|
|
|
/**
|
|
* Create a Location based on an [engine.AstNode].
|
|
*/
|
|
Location newLocation_fromNode(engine.AstNode node) {
|
|
engine.CompilationUnit unit =
|
|
node.getAncestor((node) => node is engine.CompilationUnit);
|
|
engine.CompilationUnitElement unitElement = unit.element;
|
|
engine.AnalysisContext context = unitElement.context;
|
|
engine.Source source = unitElement.source;
|
|
engine.SourceRange range = new engine.SourceRange(node.offset, node.length);
|
|
return _locationForArgs(context, source, range);
|
|
}
|
|
|
|
/**
|
|
* Create a Location based on an [engine.CompilationUnit].
|
|
*/
|
|
Location newLocation_fromUnit(
|
|
engine.CompilationUnit unit, engine.SourceRange range) {
|
|
engine.CompilationUnitElement unitElement = unit.element;
|
|
engine.AnalysisContext context = unitElement.context;
|
|
engine.Source source = unitElement.source;
|
|
return _locationForArgs(context, source, range);
|
|
}
|
|
|
|
NavigationTarget newNavigationTarget_fromElement(
|
|
engine.Element element, int fileToIndex(String file)) {
|
|
ElementKind kind = newElementKind_fromEngine(element.kind);
|
|
Location location = newLocation_fromElement(element);
|
|
String file = location.file;
|
|
int fileIndex = fileToIndex(file);
|
|
return new NavigationTarget(kind, fileIndex, location.offset, location.length,
|
|
location.startLine, location.startColumn);
|
|
}
|
|
|
|
/**
|
|
* Construct based on an element from the analyzer engine.
|
|
*/
|
|
OverriddenMember newOverriddenMember_fromEngine(engine.Element member) {
|
|
Element element = newElement_fromEngine(member);
|
|
String className = member.enclosingElement.displayName;
|
|
return new OverriddenMember(element, className);
|
|
}
|
|
|
|
/**
|
|
* Construct based on a value from the search engine.
|
|
*/
|
|
SearchResult newSearchResult_fromMatch(engine.SearchMatch match) {
|
|
SearchResultKind kind = newSearchResultKind_fromEngine(match.kind);
|
|
Location location = newLocation_fromMatch(match);
|
|
List<Element> path = _computePath(match.element);
|
|
return new SearchResult(location, kind, !match.isResolved, path);
|
|
}
|
|
|
|
/**
|
|
* Construct based on a value from the search engine.
|
|
*/
|
|
SearchResultKind newSearchResultKind_fromEngine(engine.MatchKind kind) {
|
|
if (kind == engine.MatchKind.DECLARATION) {
|
|
return SearchResultKind.DECLARATION;
|
|
}
|
|
if (kind == engine.MatchKind.READ) {
|
|
return SearchResultKind.READ;
|
|
}
|
|
if (kind == engine.MatchKind.READ_WRITE) {
|
|
return SearchResultKind.READ_WRITE;
|
|
}
|
|
if (kind == engine.MatchKind.WRITE) {
|
|
return SearchResultKind.WRITE;
|
|
}
|
|
if (kind == engine.MatchKind.INVOCATION) {
|
|
return SearchResultKind.INVOCATION;
|
|
}
|
|
if (kind == engine.MatchKind.REFERENCE) {
|
|
return SearchResultKind.REFERENCE;
|
|
}
|
|
return SearchResultKind.UNKNOWN;
|
|
}
|
|
|
|
/**
|
|
* Construct based on a SourceRange.
|
|
*/
|
|
SourceEdit newSourceEdit_range(engine.SourceRange range, String replacement,
|
|
{String id}) {
|
|
return new SourceEdit(range.offset, range.length, replacement, id: id);
|
|
}
|
|
|
|
List<Element> _computePath(engine.Element element) {
|
|
List<Element> path = <Element>[];
|
|
while (element != null) {
|
|
path.add(newElement_fromEngine(element));
|
|
// go up
|
|
if (element is engine.PrefixElement) {
|
|
// imports are library children, but they are physically in the unit
|
|
engine.LibraryElement library = element.enclosingElement;
|
|
element = library.definingCompilationUnit;
|
|
} else {
|
|
element = element.enclosingElement;
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
|
|
String _getParametersString(engine.Element element) {
|
|
// TODO(scheglov) expose the corresponding feature from ExecutableElement
|
|
List<engine.ParameterElement> parameters;
|
|
if (element is engine.ExecutableElement) {
|
|
// valid getters don't have parameters
|
|
if (element.kind == engine.ElementKind.GETTER &&
|
|
element.parameters.isEmpty) {
|
|
return null;
|
|
}
|
|
parameters = element.parameters;
|
|
} else if (element is engine.FunctionTypeAliasElement) {
|
|
parameters = element.parameters;
|
|
} else {
|
|
return null;
|
|
}
|
|
StringBuffer sb = new StringBuffer();
|
|
String closeOptionalString = '';
|
|
for (engine.ParameterElement parameter in parameters) {
|
|
if (sb.isNotEmpty) {
|
|
sb.write(', ');
|
|
}
|
|
if (closeOptionalString.isEmpty) {
|
|
if (parameter.kind == engine.ParameterKind.NAMED) {
|
|
sb.write('{');
|
|
closeOptionalString = '}';
|
|
}
|
|
if (parameter.kind == engine.ParameterKind.POSITIONAL) {
|
|
sb.write('[');
|
|
closeOptionalString = ']';
|
|
}
|
|
}
|
|
sb.write(parameter.toString());
|
|
}
|
|
sb.write(closeOptionalString);
|
|
return '(' + sb.toString() + ')';
|
|
}
|
|
|
|
String _getReturnTypeString(engine.Element element) {
|
|
if (element is engine.ExecutableElement) {
|
|
if (element.kind == engine.ElementKind.SETTER) {
|
|
return null;
|
|
} else {
|
|
return element.returnType.toString();
|
|
}
|
|
} else if (element is engine.VariableElement) {
|
|
engine.DartType type = element.type;
|
|
return type != null ? type.displayName : 'dynamic';
|
|
} else if (element is engine.FunctionTypeAliasElement) {
|
|
return element.returnType.toString();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
String _getTypeParametersString(engine.Element element) {
|
|
List<engine.TypeParameterElement> typeParameters;
|
|
if (element is engine.ClassElement) {
|
|
typeParameters = element.typeParameters;
|
|
} else if (element is engine.FunctionTypeAliasElement) {
|
|
typeParameters = element.typeParameters;
|
|
}
|
|
if (typeParameters == null || typeParameters.isEmpty) {
|
|
return null;
|
|
}
|
|
return '<${typeParameters.join(', ')}>';
|
|
}
|
|
|
|
bool _isAbstract(engine.Element element) {
|
|
// TODO(scheglov) add isAbstract to Element API
|
|
if (element is engine.ClassElement) {
|
|
return element.isAbstract;
|
|
}
|
|
if (element is engine.MethodElement) {
|
|
return element.isAbstract;
|
|
}
|
|
if (element is engine.PropertyAccessorElement) {
|
|
return element.isAbstract;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _isConst(engine.Element element) {
|
|
// TODO(scheglov) add isConst to Element API
|
|
if (element is engine.ConstructorElement) {
|
|
return element.isConst;
|
|
}
|
|
if (element is engine.VariableElement) {
|
|
return element.isConst;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _isFinal(engine.Element element) {
|
|
// TODO(scheglov) add isFinal to Element API
|
|
if (element is engine.VariableElement) {
|
|
return element.isFinal;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _isStatic(engine.Element element) {
|
|
// TODO(scheglov) add isStatic to Element API
|
|
if (element is engine.ExecutableElement) {
|
|
return element.isStatic;
|
|
}
|
|
if (element is engine.PropertyInducingElement) {
|
|
return element.isStatic;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Creates a new [Location].
|
|
*/
|
|
Location _locationForArgs(engine.AnalysisContext context, engine.Source source,
|
|
engine.SourceRange range) {
|
|
int startLine = 0;
|
|
int startColumn = 0;
|
|
{
|
|
engine.LineInfo lineInfo = context.getLineInfo(source);
|
|
if (lineInfo != null) {
|
|
engine.LineInfo_Location offsetLocation =
|
|
lineInfo.getLocation(range.offset);
|
|
startLine = offsetLocation.lineNumber;
|
|
startColumn = offsetLocation.columnNumber;
|
|
}
|
|
}
|
|
return new Location(
|
|
source.fullName, range.offset, range.length, startLine, startColumn);
|
|
}
|