Listen for context add/remove and unit invalidation to update the index.

R=brianwilkerson@google.com
BUG=

Review URL: https://codereview.chromium.org/1783503004 .
This commit is contained in:
Konstantin Shcheglov 2016-03-09 14:52:19 -08:00
parent 9292d1bc2b
commit 4d48fa56d0
7 changed files with 108 additions and 36 deletions

View file

@ -39,6 +39,7 @@ import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
import 'package:analyzer/src/task/dart.dart';
import 'package:analyzer/src/util/glob.dart';
import 'package:analyzer/task/dart.dart';
import 'package:plugin/plugin.dart';
typedef void OptionUpdater(AnalysisOptionsImpl options);
@ -83,7 +84,7 @@ class AnalysisServer {
* a 1 millisecond delay so that the VM and dart:io can deliver content
* to stdin. This should be removed once the underlying problem is fixed.
*/
static int performOperationDelayFreqency = 25;
static int performOperationDelayFrequency = 25;
/**
* The options of this server instance.
@ -354,6 +355,7 @@ class AnalysisServer {
_performance = performanceAfterStartup;
});
});
_setupIndexInvalidation();
Notification notification =
new ServerConnectedParams(VERSION).toNotification();
channel.sendNotification(notification);
@ -675,23 +677,6 @@ class AnalysisServer {
return context.getErrors(source);
}
// TODO(brianwilkerson) Add the following method after 'prioritySources' has
// been added to InternalAnalysisContext.
// /**
// * Return a list containing the full names of all of the sources that are
// * priority sources.
// */
// List<String> getPriorityFiles() {
// List<String> priorityFiles = new List<String>();
// folderMap.values.forEach((ContextDirectory directory) {
// InternalAnalysisContext context = directory.context;
// context.prioritySources.forEach((Source source) {
// priorityFiles.add(source.fullName);
// });
// });
// return priorityFiles;
// }
/**
* Returns resolved [AstNode]s at the given [offset] of the given [file].
*
@ -709,6 +694,23 @@ class AnalysisServer {
return nodes;
}
// TODO(brianwilkerson) Add the following method after 'prioritySources' has
// been added to InternalAnalysisContext.
// /**
// * Return a list containing the full names of all of the sources that are
// * priority sources.
// */
// List<String> getPriorityFiles() {
// List<String> priorityFiles = new List<String>();
// folderMap.values.forEach((ContextDirectory directory) {
// InternalAnalysisContext context = directory.context;
// context.prioritySources.forEach((Source source) {
// priorityFiles.add(source.fullName);
// });
// });
// return priorityFiles;
// }
/**
* Returns resolved [CompilationUnit]s of the Dart file with the given [path].
*
@ -1452,14 +1454,42 @@ class AnalysisServer {
*/
int now = new DateTime.now().millisecondsSinceEpoch;
if (now > _nextPerformOperationDelayTime &&
performOperationDelayFreqency > 0) {
_nextPerformOperationDelayTime = now + performOperationDelayFreqency;
performOperationDelayFrequency > 0) {
_nextPerformOperationDelayTime = now + performOperationDelayFrequency;
new Future.delayed(new Duration(milliseconds: 1), performOperation);
} else {
new Future(performOperation);
}
performOperationPending = true;
}
/**
* Listen for context events and invalidate index.
*
* It is possible that this method will do more in the future, e.g. listening
* for summary information and linking pre-indexed packages into the index,
* but for now we only invalidate project specific index information.
*/
void _setupIndexInvalidation() {
if (index2 == null) {
return;
}
onContextsChanged.listen((ContextsChangedEvent event) {
for (AnalysisContext context in event.added) {
context
.onResultChanged(RESOLVED_UNIT)
.listen((ResultChangedEvent event) {
if (event.wasInvalidated) {
LibrarySpecificUnit target = event.target;
index2.removeUnit(event.context, target.library, target.unit);
}
});
}
for (AnalysisContext context in event.removed) {
index2.removeContext(context);
}
});
}
}
class AnalysisServerOptions {
@ -1662,7 +1692,7 @@ class ServerPerformance {
int slowRequestCount = 0;
/**
* Log performation information about the given request.
* Log performance information about the given request.
*/
void logRequest(Request request) {
++requestCount;

View file

@ -343,7 +343,7 @@ class Driver implements ServerStarter {
// TODO (danrubel) Remove this workaround
// once the underlying VM and dart:io issue has been fixed.
if (results[INTERNAL_DELAY_FREQUENCY] != null) {
AnalysisServer.performOperationDelayFreqency =
AnalysisServer.performOperationDelayFrequency =
int.parse(results[INTERNAL_DELAY_FREQUENCY], onError: (_) => 0);
}

View file

@ -94,6 +94,14 @@ class Index2 {
_contextIndexMap.remove(context);
}
/**
* Remove index information about the unit in the given [context].
*/
void removeUnit(
AnalysisContext context, Source librarySource, Source unitSource) {
_contextIndexMap[context]?.removeUnit(librarySource, unitSource);
}
/**
* Return the [_ContextIndex] instance for the given [context].
*/
@ -207,8 +215,7 @@ class _ContextIndex {
*/
Future<List<Location>> getDefinedNames(
RegExp regExp, IndexNameKind kind) async {
return _mergeLocations((PackageIndex index) {
_PackageIndexRequester requester = new _PackageIndexRequester(index);
return _mergeLocations((_PackageIndexRequester requester) {
return requester.getDefinedNames(context, regExp, kind);
});
}
@ -218,8 +225,7 @@ class _ContextIndex {
* of the given [kind].
*/
Future<List<Location>> getRelations(Element element, IndexRelationKind kind) {
return _mergeLocations((PackageIndex index) {
_PackageIndexRequester requester = new _PackageIndexRequester(index);
return _mergeLocations((_PackageIndexRequester requester) {
return requester.getRelations(context, element, kind);
});
}
@ -229,8 +235,7 @@ class _ContextIndex {
* [name] is referenced with a qualifier, but is not resolved.
*/
Future<List<Location>> getUnresolvedMemberReferences(String name) async {
return _mergeLocations((PackageIndex index) {
_PackageIndexRequester requester = new _PackageIndexRequester(index);
return _mergeLocations((_PackageIndexRequester requester) {
return requester.getUnresolvedMemberReferences(context, name);
});
}
@ -250,6 +255,14 @@ class _ContextIndex {
indexMap[key] = index;
}
/**
* Remove index information about the unit.
*/
void removeUnit(Source librarySource, Source unitSource) {
String key = _getUnitKeyForSource(librarySource, unitSource);
indexMap.remove(key);
}
String _getUnitKeyForElement(CompilationUnitElement unitElement) {
Source librarySource = unitElement.library.source;
Source unitSource = unitElement.source;
@ -263,10 +276,11 @@ class _ContextIndex {
}
Future<List<Location>> _mergeLocations(
List<Location> callback(PackageIndex index)) async {
List<Location> callback(_PackageIndexRequester requester)) async {
List<Location> locations = <Location>[];
for (PackageIndex index in indexMap.values) {
List<Location> indexLocations = callback(index);
_PackageIndexRequester requester = new _PackageIndexRequester(index);
List<Location> indexLocations = callback(requester);
locations.addAll(indexLocations);
}
return locations;

View file

@ -22,10 +22,9 @@ import 'package:analyzer/src/summary/idl.dart';
* A [SearchEngine] implementation.
*/
class SearchEngineImpl2 implements SearchEngine {
final AnalysisContext context;
final Index2 _index;
SearchEngineImpl2(this.context, this._index);
SearchEngineImpl2(this._index);
@override
Future<List<SearchMatch>> searchAllSubtypes(ClassElement type) async {
@ -111,7 +110,7 @@ class SearchEngineImpl2 implements SearchEngine {
SearchMatch _newMatchForLocation(Location location, MatchKind kind) =>
new SearchMatch(
context,
location.context,
location.libraryUri,
location.unitUri,
kind,
@ -176,6 +175,7 @@ class SearchEngineImpl2 implements SearchEngine {
List<SearchMatch> matches = <SearchMatch>[];
LibraryElement libraryElement = element.library;
Source librarySource = libraryElement.source;
AnalysisContext context = libraryElement.context;
for (CompilationUnitElement unitElement in libraryElement.units) {
Source unitSource = unitElement.source;
CompilationUnit unit =
@ -193,6 +193,7 @@ class SearchEngineImpl2 implements SearchEngine {
List<SearchMatch> matches = <SearchMatch>[];
LibraryElement libraryElement = element.library;
Source librarySource = libraryElement.source;
AnalysisContext context = libraryElement.context;
for (CompilationUnitElement unitElement in libraryElement.parts) {
Source unitSource = unitElement.source;
CompilationUnit unit =
@ -237,6 +238,7 @@ class SearchEngineImpl2 implements SearchEngine {
List<SearchMatch> matches = <SearchMatch>[];
LibraryElement libraryElement = element.library;
Source librarySource = libraryElement.source;
AnalysisContext context = libraryElement.context;
for (CompilationUnitElement unitElement in libraryElement.units) {
Source unitSource = unitElement.source;
CompilationUnit unit =

View file

@ -1707,7 +1707,7 @@ class GetHandler {
// TODO(brianwilkerson) Add items for the SDK contexts (currently only one).
buffer.write('</p>');
int freq = AnalysisServer.performOperationDelayFreqency;
int freq = AnalysisServer.performOperationDelayFrequency;
String delay = freq > 0 ? '1 ms every $freq ms' : 'off';
buffer.write('<p><b>Performance Data</b></p>');

View file

@ -197,12 +197,38 @@ class A {}
RegExp regExp = new RegExp(r'^A$');
expect(await index.getDefinedNames(regExp, IndexNameKind.topLevel),
hasLength(1));
// remove the context - no
// remove the context - no top-level declarations
index.removeContext(context);
expect(
await index.getDefinedNames(regExp, IndexNameKind.topLevel), isEmpty);
}
test_removeUnit() async {
RegExp regExp = new RegExp(r'^[AB]$');
Source sourceA = addSource('/a.dart', 'class A {}');
Source sourceB = addSource('/b.dart', 'class B {}');
CompilationUnit unitA = resolveLibraryUnit(sourceA);
CompilationUnit unitB = resolveLibraryUnit(sourceB);
index.indexUnit(unitA);
index.indexUnit(unitB);
{
List<Location> locations =
await index.getDefinedNames(regExp, IndexNameKind.topLevel);
expect(locations, hasLength(2));
expect(locations.map((l) => l.libraryUri),
unorderedEquals([sourceA.uri.toString(), sourceB.uri.toString()]));
}
// remove a.dart - no a.dart location
index.removeUnit(context, sourceA, sourceA);
{
List<Location> locations =
await index.getDefinedNames(regExp, IndexNameKind.topLevel);
expect(locations, hasLength(1));
expect(locations.map((l) => l.libraryUri),
unorderedEquals([sourceB.uri.toString()]));
}
}
/**
* Assert that the given list of [locations] has a [Location] corresponding
* to the [element].

View file

@ -70,7 +70,7 @@ class SearchEngineImpl2Test extends AbstractSingleUnitTest {
void setUp() {
super.setUp();
index = createMemoryIndex2();
searchEngine = new SearchEngineImpl2(context, index);
searchEngine = new SearchEngineImpl2(index);
}
test_searchAllSubtypes() async {