blob: 65be43a555dd15cd42bc039f4fa80ea86c674dc3 [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 'dart:collection';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/protocol_server.dart' as protocol;
import 'package:analysis_server/src/search/element_references.dart';
import 'package:analysis_server/src/search/type_hierarchy.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/analysis/search.dart' as search;
/**
* Instances of the class [SearchDomainHandler] implement a [RequestHandler]
* that handles requests in the search domain.
*/
class SearchDomainHandler implements protocol.RequestHandler {
/**
* The analysis server that is using this handler to process requests.
*/
final AnalysisServer server;
/**
* The [SearchEngine] for this server.
*/
final SearchEngine searchEngine;
/**
* The next search response id.
*/
int _nextSearchId = 0;
/**
* Initialize a newly created handler to handle requests for the given [server].
*/
SearchDomainHandler(AnalysisServer server)
: server = server,
searchEngine = server.searchEngine;
Future findElementReferences(protocol.Request request) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
var params =
new protocol.SearchFindElementReferencesParams.fromRequest(request);
String file = params.file;
// prepare element
Element element = await server.getElementAtOffset(file, params.offset);
if (element is ImportElement) {
element = (element as ImportElement).prefix;
}
if (element is FieldFormalParameterElement) {
element = (element as FieldFormalParameterElement).field;
}
if (element is PropertyAccessorElement) {
element = (element as PropertyAccessorElement).variable;
}
// respond
String searchId = (_nextSearchId++).toString();
var result = new protocol.SearchFindElementReferencesResult();
if (element != null) {
result.id = searchId;
result.element = protocol.convertElement(element);
}
_sendSearchResult(request, result);
// search elements
if (element != null) {
var computer = new ElementReferencesComputer(searchEngine);
List<protocol.SearchResult> results =
await computer.compute(element, params.includePotential);
_sendSearchNotification(searchId, true, results);
}
}
Future findMemberDeclarations(protocol.Request request) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
var params =
new protocol.SearchFindMemberDeclarationsParams.fromRequest(request);
await server.onAnalysisComplete;
// respond
String searchId = (_nextSearchId++).toString();
_sendSearchResult(
request, new protocol.SearchFindMemberDeclarationsResult(searchId));
// search
List<SearchMatch> matches =
await searchEngine.searchMemberDeclarations(params.name);
matches = SearchMatch.withNotNullElement(matches);
_sendSearchNotification(searchId, true, matches.map(toResult));
}
Future findMemberReferences(protocol.Request request) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
var params =
new protocol.SearchFindMemberReferencesParams.fromRequest(request);
await server.onAnalysisComplete;
// respond
String searchId = (_nextSearchId++).toString();
_sendSearchResult(
request, new protocol.SearchFindMemberReferencesResult(searchId));
// search
List<SearchMatch> matches =
await searchEngine.searchMemberReferences(params.name);
matches = SearchMatch.withNotNullElement(matches);
_sendSearchNotification(searchId, true, matches.map(toResult));
}
Future findTopLevelDeclarations(protocol.Request request) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
var params =
new protocol.SearchFindTopLevelDeclarationsParams.fromRequest(request);
try {
// validate the regex
new RegExp(params.pattern);
} on FormatException catch (exception) {
server.sendResponse(new protocol.Response.invalidParameter(
request, 'pattern', exception.message));
return;
}
await server.onAnalysisComplete;
// respond
String searchId = (_nextSearchId++).toString();
_sendSearchResult(
request, new protocol.SearchFindTopLevelDeclarationsResult(searchId));
// search
List<SearchMatch> matches =
await searchEngine.searchTopLevelDeclarations(params.pattern);
matches = SearchMatch.withNotNullElement(matches);
_sendSearchNotification(searchId, true, matches.map(toResult));
}
/**
* Implement the `search.getDeclarations` request.
*/
Future getDeclarations(protocol.Request request) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
var params =
new protocol.SearchGetElementDeclarationsParams.fromRequest(request);
RegExp regExp;
if (params.pattern != null) {
try {
regExp = new RegExp(params.pattern);
} on FormatException catch (exception) {
server.sendResponse(new protocol.Response.invalidParameter(
request, 'pattern', exception.message));
return;
}
}
var files = new LinkedHashSet<String>();
var declarations = <search.Declaration>[];
protocol.ElementKind getElementKind(search.DeclarationKind kind) {
switch (kind) {
case search.DeclarationKind.CLASS:
return protocol.ElementKind.CLASS;
case search.DeclarationKind.CLASS_TYPE_ALIAS:
return protocol.ElementKind.CLASS_TYPE_ALIAS;
case search.DeclarationKind.CONSTRUCTOR:
return protocol.ElementKind.CONSTRUCTOR;
case search.DeclarationKind.ENUM:
return protocol.ElementKind.ENUM;
case search.DeclarationKind.ENUM_CONSTANT:
return protocol.ElementKind.ENUM_CONSTANT;
case search.DeclarationKind.FIELD:
return protocol.ElementKind.FIELD;
case search.DeclarationKind.FUNCTION:
return protocol.ElementKind.FUNCTION;
case search.DeclarationKind.FUNCTION_TYPE_ALIAS:
return protocol.ElementKind.FUNCTION_TYPE_ALIAS;
case search.DeclarationKind.GETTER:
return protocol.ElementKind.GETTER;
case search.DeclarationKind.METHOD:
return protocol.ElementKind.METHOD;
case search.DeclarationKind.MIXIN:
return protocol.ElementKind.MIXIN;
case search.DeclarationKind.SETTER:
return protocol.ElementKind.SETTER;
case search.DeclarationKind.VARIABLE:
return protocol.ElementKind.TOP_LEVEL_VARIABLE;
default:
return protocol.ElementKind.CLASS;
}
}
int remainingMaxResults = params.maxResults;
for (var driver in server.driverMap.values.toList()) {
var driverDeclarations = await driver.search.declarations(
regExp, remainingMaxResults, files,
onlyForFile: params.file);
declarations.addAll(driverDeclarations);
if (remainingMaxResults != null) {
remainingMaxResults -= driverDeclarations.length;
if (remainingMaxResults <= 0) {
break;
}
}
}
List<protocol.ElementDeclaration> elementDeclarations =
declarations.map((declaration) {
return new protocol.ElementDeclaration(
declaration.name,
getElementKind(declaration.kind),
declaration.fileIndex,
declaration.offset,
declaration.line,
declaration.column,
declaration.codeOffset,
declaration.codeLength,
className: declaration.className,
mixinName: declaration.mixinName,
parameters: declaration.parameters);
}).toList();
server.sendResponse(new protocol.SearchGetElementDeclarationsResult(
elementDeclarations, files.toList())
.toResponse(request.id));
}
/**
* Implement the `search.getTypeHierarchy` request.
*/
Future getTypeHierarchy(protocol.Request request) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
var params = new protocol.SearchGetTypeHierarchyParams.fromRequest(request);
String file = params.file;
// prepare element
Element element = await server.getElementAtOffset(file, params.offset);
if (element == null) {
_sendTypeHierarchyNull(request);
return;
}
// maybe supertype hierarchy only
if (params.superOnly == true) {
TypeHierarchyComputer computer =
new TypeHierarchyComputer(searchEngine, element);
List<protocol.TypeHierarchyItem> items = computer.computeSuper();
protocol.Response response =
new protocol.SearchGetTypeHierarchyResult(hierarchyItems: items)
.toResponse(request.id);
server.sendResponse(response);
return;
}
// prepare type hierarchy
TypeHierarchyComputer computer =
new TypeHierarchyComputer(searchEngine, element);
List<protocol.TypeHierarchyItem> items = await computer.compute();
protocol.Response response =
new protocol.SearchGetTypeHierarchyResult(hierarchyItems: items)
.toResponse(request.id);
server.sendResponse(response);
}
@override
protocol.Response handleRequest(protocol.Request request) {
try {
String requestName = request.method;
if (requestName == SEARCH_REQUEST_FIND_ELEMENT_REFERENCES) {
findElementReferences(request);
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_FIND_MEMBER_DECLARATIONS) {
findMemberDeclarations(request);
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_FIND_MEMBER_REFERENCES) {
findMemberReferences(request);
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_FIND_TOP_LEVEL_DECLARATIONS) {
findTopLevelDeclarations(request);
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_GET_ELEMENT_DECLARATIONS) {
getDeclarations(request);
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_GET_TYPE_HIERARCHY) {
getTypeHierarchy(request);
return protocol.Response.DELAYED_RESPONSE;
}
} on protocol.RequestFailure catch (exception) {
return exception.response;
}
return null;
}
void _sendSearchNotification(
String searchId, bool isLast, Iterable<protocol.SearchResult> results) {
server.sendNotification(
new protocol.SearchResultsParams(searchId, results.toList(), isLast)
.toNotification());
}
/**
* Send a search response with the given [result] to the given [request].
*/
void _sendSearchResult(protocol.Request request, result) {
protocol.Response response = result.toResponse(request.id);
server.sendResponse(response);
}
void _sendTypeHierarchyNull(protocol.Request request) {
protocol.Response response =
new protocol.SearchGetTypeHierarchyResult().toResponse(request.id);
server.sendResponse(response);
}
static protocol.SearchResult toResult(SearchMatch match) {
return protocol.newSearchResult_fromMatch(match);
}
}