blob: 7d77d4a06a22b7bb6fd9c7de9f7461ab6313d46c [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.
library operation.analysis;
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/computer/computer_highlights.dart';
import 'package:analysis_server/src/computer/computer_navigation.dart';
import 'package:analysis_server/src/computer/computer_occurrences.dart';
import 'package:analysis_server/src/computer/computer_outline.dart';
import 'package:analysis_server/src/computer/computer_overrides.dart';
import 'package:analysis_server/src/operation/operation.dart';
import 'package:analysis_server/src/protocol_server.dart' as protocol;
import 'package:analysis_server/src/services/index/index.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/html.dart';
import 'package:analyzer/src/generated/source.dart';
/**
* Schedules sending notifications for the given [file] using the resolved
* [resolvedDartUnit].
*/
void scheduleNotificationOperations(AnalysisServer server, String file,
LineInfo lineInfo, AnalysisContext context, CompilationUnit parsedDartUnit,
CompilationUnit resolvedDartUnit, List<AnalysisError> errors) {
// Only send notifications if the current context is the preferred
// context for the file. This avoids redundant notification messages
// being sent to the client (see dartbug.com/22210).
// TODO(paulberry): note that there is a small risk that this will cause
// notifications to be lost if the preferred context for a file changes
// while analysis is in progress (e.g. because the client sent an
// analysis.setAnalysisRoots message).
if (server.getAnalysisContext(file) != context) {
return;
}
// Dart
CompilationUnit dartUnit =
resolvedDartUnit != null ? resolvedDartUnit : parsedDartUnit;
if (resolvedDartUnit != null) {
if (server.hasAnalysisSubscription(
protocol.AnalysisService.HIGHLIGHTS,
file)) {
server.scheduleOperation(
new _DartHighlightsOperation(file, resolvedDartUnit));
}
if (server.hasAnalysisSubscription(
protocol.AnalysisService.NAVIGATION,
file)) {
server.scheduleOperation(
new _DartNavigationOperation(file, resolvedDartUnit));
}
if (server.hasAnalysisSubscription(
protocol.AnalysisService.OCCURRENCES,
file)) {
server.scheduleOperation(
new _DartOccurrencesOperation(file, resolvedDartUnit));
}
if (server.hasAnalysisSubscription(
protocol.AnalysisService.OVERRIDES,
file)) {
server.scheduleOperation(
new _DartOverridesOperation(file, resolvedDartUnit));
}
}
if (dartUnit != null) {
if (server.hasAnalysisSubscription(
protocol.AnalysisService.OUTLINE,
file)) {
server.scheduleOperation(
new _DartOutlineOperation(file, lineInfo, dartUnit));
}
}
// errors
if (server.shouldSendErrorsNotificationFor(file)) {
server.scheduleOperation(
new _NotificationErrorsOperation(file, lineInfo, errors));
}
}
void sendAnalysisNotificationErrors(AnalysisServer server, String file,
LineInfo lineInfo, List<AnalysisError> errors) {
try {
if (errors == null) {
errors = <AnalysisError>[];
}
var serverErrors =
protocol.doAnalysisError_listFromEngine(lineInfo, errors);
var params = new protocol.AnalysisErrorsParams(file, serverErrors);
server.sendNotification(params.toNotification());
} catch (exception, stackTrace) {
server.sendServerErrorNotification(exception, stackTrace);
}
}
void sendAnalysisNotificationHighlights(AnalysisServer server, String file,
CompilationUnit dartUnit) {
try {
var regions = new DartUnitHighlightsComputer(dartUnit).compute();
var params = new protocol.AnalysisHighlightsParams(file, regions);
server.sendNotification(params.toNotification());
} catch (exception, stackTrace) {
server.sendServerErrorNotification(exception, stackTrace);
}
}
void sendAnalysisNotificationNavigation(AnalysisServer server, String file,
CompilationUnit dartUnit) {
try {
var computer = new DartUnitNavigationComputer(dartUnit);
computer.compute();
var params = new protocol.AnalysisNavigationParams(
file,
computer.regions,
computer.targets,
computer.files);
server.sendNotification(params.toNotification());
} catch (exception, stackTrace) {
server.sendServerErrorNotification(exception, stackTrace);
}
}
void sendAnalysisNotificationOccurrences(AnalysisServer server, String file,
CompilationUnit dartUnit) {
try {
var occurrences = new DartUnitOccurrencesComputer(dartUnit).compute();
var params = new protocol.AnalysisOccurrencesParams(file, occurrences);
server.sendNotification(params.toNotification());
} catch (exception, stackTrace) {
server.sendServerErrorNotification(exception, stackTrace);
}
}
void sendAnalysisNotificationOutline(AnalysisServer server, String file,
LineInfo lineInfo, CompilationUnit dartUnit) {
try {
var computer = new DartUnitOutlineComputer(file, lineInfo, dartUnit);
var outline = computer.compute();
var params = new protocol.AnalysisOutlineParams(file, outline);
server.sendNotification(params.toNotification());
} catch (exception, stackTrace) {
server.sendServerErrorNotification(exception, stackTrace);
}
}
void sendAnalysisNotificationOverrides(AnalysisServer server, String file,
CompilationUnit dartUnit) {
try {
var overrides = new DartUnitOverridesComputer(dartUnit).compute();
var params = new protocol.AnalysisOverridesParams(file, overrides);
server.sendNotification(params.toNotification());
} catch (exception, stackTrace) {
server.sendServerErrorNotification(exception, stackTrace);
}
}
/**
* Instances of [PerformAnalysisOperation] perform a single analysis task.
*/
class PerformAnalysisOperation extends ServerOperation {
static const int IDLE_CACHE_SIZE = AnalysisOptionsImpl.DEFAULT_CACHE_SIZE;
static const int WORKING_CACHE_SIZE = 512;
final AnalysisContext context;
final bool isContinue;
PerformAnalysisOperation(this.context, this.isContinue);
@override
ServerOperationPriority get priority {
if (_isPriorityContext) {
if (isContinue) {
return ServerOperationPriority.PRIORITY_ANALYSIS_CONTINUE;
} else {
return ServerOperationPriority.PRIORITY_ANALYSIS;
}
} else {
if (isContinue) {
return ServerOperationPriority.ANALYSIS_CONTINUE;
} else {
return ServerOperationPriority.ANALYSIS;
}
}
}
bool get _isPriorityContext =>
context is InternalAnalysisContext &&
(context as InternalAnalysisContext).prioritySources.isNotEmpty;
@override
void perform(AnalysisServer server) {
//
// TODO(brianwilkerson) Add an optional function-valued parameter to
// performAnalysisTask that will be called when the task has been computed
// but before it is performed and send notification in the function:
//
// AnalysisResult result = context.performAnalysisTask((taskDescription) {
// sendStatusNotification(context.toString(), taskDescription);
// });
if (!isContinue) {
_setCacheSize(WORKING_CACHE_SIZE);
}
// prepare results
AnalysisResult result = context.performAnalysisTask();
List<ChangeNotice> notices = result.changeNotices;
if (notices == null) {
_setCacheSize(IDLE_CACHE_SIZE);
server.sendContextAnalysisDoneNotifications(
context,
AnalysisDoneReason.COMPLETE);
return;
}
// process results
_sendNotices(server, notices);
_updateIndex(server, notices);
// continue analysis
server.addOperation(new PerformAnalysisOperation(context, true));
}
/**
* Send the information in the given list of notices back to the client.
*/
void _sendNotices(AnalysisServer server, List<ChangeNotice> notices) {
for (int i = 0; i < notices.length; i++) {
ChangeNotice notice = notices[i];
Source source = notice.source;
String file = source.fullName;
// Dart
CompilationUnit parsedDartUnit = notice.parsedDartUnit;
CompilationUnit resolvedDartUnit = notice.resolvedDartUnit;
scheduleNotificationOperations(
server,
file,
notice.lineInfo,
context,
parsedDartUnit,
resolvedDartUnit,
notice.errors);
// done
server.fileAnalyzed(notice);
}
}
void _setCacheSize(int cacheSize) {
AnalysisOptionsImpl options =
new AnalysisOptionsImpl.con1(context.analysisOptions);
options.cacheSize = cacheSize;
context.analysisOptions = options;
}
void _updateIndex(AnalysisServer server, List<ChangeNotice> notices) {
Index index = server.index;
if (index == null) {
return;
}
for (ChangeNotice notice in notices) {
String file = notice.source.fullName;
// Dart
try {
CompilationUnit dartUnit = notice.resolvedDartUnit;
if (dartUnit != null) {
server.addOperation(new _DartIndexOperation(context, file, dartUnit));
}
} catch (exception, stackTrace) {
server.sendServerErrorNotification(exception, stackTrace);
}
// HTML
try {
HtmlUnit htmlUnit = notice.resolvedHtmlUnit;
if (htmlUnit != null) {
server.addOperation(new _HtmlIndexOperation(context, file, htmlUnit));
}
} catch (exception, stackTrace) {
server.sendServerErrorNotification(exception, stackTrace);
}
}
}
}
class _DartHighlightsOperation extends _DartNotificationOperation {
_DartHighlightsOperation(String file, CompilationUnit unit)
: super(file, unit);
@override
void perform(AnalysisServer server) {
sendAnalysisNotificationHighlights(server, file, unit);
}
}
class _DartIndexOperation extends _SingleFileOperation {
final AnalysisContext context;
final CompilationUnit unit;
_DartIndexOperation(this.context, String file, this.unit) : super(file);
@override
ServerOperationPriority get priority {
return ServerOperationPriority.ANALYSIS_INDEX;
}
@override
void perform(AnalysisServer server) {
Index index = server.index;
index.indexUnit(context, unit);
}
}
class _DartNavigationOperation extends _DartNotificationOperation {
_DartNavigationOperation(String file, CompilationUnit unit)
: super(file, unit);
@override
void perform(AnalysisServer server) {
sendAnalysisNotificationNavigation(server, file, unit);
}
}
abstract class _DartNotificationOperation extends _SingleFileOperation {
final CompilationUnit unit;
_DartNotificationOperation(String file, this.unit) : super(file);
@override
ServerOperationPriority get priority {
return ServerOperationPriority.ANALYSIS_NOTIFICATION;
}
}
class _DartOccurrencesOperation extends _DartNotificationOperation {
_DartOccurrencesOperation(String file, CompilationUnit unit)
: super(file, unit);
@override
void perform(AnalysisServer server) {
sendAnalysisNotificationOccurrences(server, file, unit);
}
}
class _DartOutlineOperation extends _DartNotificationOperation {
final LineInfo lineInfo;
_DartOutlineOperation(String file, this.lineInfo, CompilationUnit unit)
: super(file, unit);
@override
void perform(AnalysisServer server) {
sendAnalysisNotificationOutline(server, file, lineInfo, unit);
}
}
class _DartOverridesOperation extends _DartNotificationOperation {
_DartOverridesOperation(String file, CompilationUnit unit)
: super(file, unit);
@override
void perform(AnalysisServer server) {
sendAnalysisNotificationOverrides(server, file, unit);
}
}
class _HtmlIndexOperation extends _SingleFileOperation {
final AnalysisContext context;
final HtmlUnit unit;
_HtmlIndexOperation(this.context, String file, this.unit) : super(file);
@override
ServerOperationPriority get priority {
return ServerOperationPriority.ANALYSIS_INDEX;
}
@override
void perform(AnalysisServer server) {
Index index = server.index;
index.indexHtmlUnit(context, unit);
}
}
class _NotificationErrorsOperation extends _SingleFileOperation {
final LineInfo lineInfo;
final List<AnalysisError> errors;
_NotificationErrorsOperation(String file, this.lineInfo, this.errors)
: super(file);
@override
ServerOperationPriority get priority {
return ServerOperationPriority.ANALYSIS_NOTIFICATION;
}
@override
void perform(AnalysisServer server) {
sendAnalysisNotificationErrors(server, file, lineInfo, errors);
}
}
abstract class _SingleFileOperation extends SourceSensitiveOperation {
final String file;
_SingleFileOperation(this.file);
@override
bool shouldBeDiscardedOnSourceChange(Source source) {
return source.fullName == file;
}
}