Skip to content

Commit

Permalink
suggest after import prefix in part files - fixes #25023
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Rubel committed Dec 9, 2015
1 parent 1f5973e commit 830a37a
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ abstract class DartCompletionRequest extends CompletionRequest {
*/
Future<CompilationUnit> resolveDeclarationsInScope();

/**
* Return a [Future] that completes with a list of directives for the library
* in which in which the completion is occurring.
* The [Future] may return `null` if the library unit cannot be determined
* (e.g. unlinked part file).
* Any information obtained from [target] prior to calling this method
* should be discarded as it may have changed.
*/
Future<List<Directive>> resolveDirectives();

/**
* Return a [Future] that completes when the element associated with
* the given [expression] in the target compilation unit is available.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ class DartCompletionManager implements CompletionContributor {
*/
class DartCompletionRequestImpl extends CompletionRequestImpl
implements DartCompletionRequest {
/**
* The source for the library containing the completion request.
* This may be different from the source in which the completion is requested
* if the completion is being requested in a part file.
* This may be `null` if the library for a part file cannot be determined.
*/
Source _librarySource;

/**
* The cached completion target or `null` if not computed yet.
*/
Expand Down Expand Up @@ -93,7 +101,16 @@ class DartCompletionRequestImpl extends CompletionRequestImpl
SearchEngine searchEngine,
Source source,
int offset)
: super(context, resourceProvider, searchEngine, source, offset);
: super(context, resourceProvider, searchEngine, source, offset) {
if (target.unit.directives.any((d) => d is PartOfDirective)) {
List<Source> libraries = context.getLibrariesContaining(source);
if (libraries.isNotEmpty) {
_librarySource = libraries[0];
}
} else {
_librarySource = source;
}
}

@override
Future<LibraryElement> get libraryElement async {
Expand Down Expand Up @@ -134,24 +151,16 @@ class DartCompletionRequestImpl extends CompletionRequestImpl
return unit;
}

// Determine the library source
Source librarySource;
if (unit.directives.any((d) => d is PartOfDirective)) {
List<Source> libraries = context.getLibrariesContaining(source);
if (libraries.isEmpty) {
return null;
}
librarySource = libraries[0];
} else {
librarySource = source;
// Gracefully degrade if librarySource cannot be determined
if (_librarySource == null) {
return null;
}

// Resolve declarations in the target unit
CompilationUnit resolvedUnit =
await new AnalysisFutureHelper<CompilationUnit>(
context,
new LibrarySpecificUnit(librarySource, source),
RESOLVED_UNIT3).computeAsync();
await new AnalysisFutureHelper<CompilationUnit>(context,
new LibrarySpecificUnit(_librarySource, source), RESOLVED_UNIT3)
.computeAsync();

// TODO(danrubel) determine if the underlying source has been modified
// in a way that invalidates the completion request
Expand All @@ -168,39 +177,45 @@ class DartCompletionRequestImpl extends CompletionRequestImpl
return resolvedUnit;
}

@override
Future<List<Directive>> resolveDirectives() async {
CompilationUnit libUnit;
if (_librarySource == source) {
libUnit = await resolveDeclarationsInScope();
} else if (_librarySource != null) {
libUnit =
await new AnalysisFutureHelper<CompilationUnit>(
context,
new LibrarySpecificUnit(_librarySource, _librarySource),
RESOLVED_UNIT3)
.computeAsync();
}
return libUnit?.directives;
}

@override
Future resolveExpression(Expression expression) async {
//TODO(danrubel) resolve the expression or containing method
// rather than the entire complilation unit

CompilationUnit unit = target.unit;

// Determine the library source
Source librarySource;
if (unit.directives.any((d) => d is PartOfDirective)) {
List<Source> libraries = context.getLibrariesContaining(source);
if (libraries.isEmpty) {
return;
}
librarySource = libraries[0];
} else {
librarySource = source;
// Gracefully degrade if librarySource cannot be determined
if (_librarySource == null) {
return null;
}

// Resolve declarations in the target unit
CompilationUnit resolvedUnit =
await new AnalysisFutureHelper<CompilationUnit>(
context,
new LibrarySpecificUnit(librarySource, source),
RESOLVED_UNIT).computeAsync();
await new AnalysisFutureHelper<CompilationUnit>(context,
new LibrarySpecificUnit(_librarySource, source), RESOLVED_UNIT)
.computeAsync();

// TODO(danrubel) determine if the underlying source has been modified
// in a way that invalidates the completion request
// and return null

// Gracefully degrade if unit cannot be resolved
if (resolvedUnit == null) {
return;
return null;
}

// Recompute the target for the newly resolved unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ class LibraryMemberContributor extends DartCompletionContributor {
// Resolve the expression and the containing library
await request.resolveExpression(targetId);
LibraryElement containingLibrary = await request.libraryElement;
// Gracefully degrade if the library could not be determined
List<Directive> directives = await request.resolveDirectives();
// Gracefully degrade if the library or directives could not be determined
// e.g. detached part file or source change
if (containingLibrary == null) {
if (containingLibrary == null || directives == null) {
return EMPTY_LIST;
}

Expand All @@ -51,7 +52,7 @@ class LibraryMemberContributor extends DartCompletionContributor {
List<CompletionSuggestion> suggestions = <CompletionSuggestion>[];

// Find the import directive with the given prefix
for (Directive directive in request.target.unit.directives) {
for (Directive directive in directives) {
if (directive is ImportDirective) {
if (directive.prefix != null) {
if (directive.prefix.name == elem.name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,9 @@ abstract class DartCompletionContributorTest extends AbstractContextTest {
if (libraries.isNotEmpty) {
return new Future.value(libraries);
}
if (times == 0) {
fail('failed to determine libraries containing $testSource');
}
context.performAnalysisTask();
// We use a delayed future to allow microtask events to finish. The
// Future.value or Future() constructors use scheduleMicrotask themselves and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ class LibraryMemberContributorTest extends DartCompletionContributorTest {
assertSuggestFunction('loadLibrary', 'Future<dynamic>');
}

test_libraryPrefix_deferred_inPart() async {
// SimpleIdentifier PrefixedIdentifier ExpressionStatement
var libFile = '${testFile.substring(0, testFile.length - 5)}A.dart';
addSource(
libFile,
'''
library testA;
import "dart:async" deferred as bar;
part "$testFile";''');
addTestSource('part of testA; foo() {bar.^}');
// Assume that libraries containing has been computed for part files
await computeLibrariesContaining();
await computeSuggestions();
assertSuggestClass('Future');
assertSuggestFunction('loadLibrary', 'Future<dynamic>');
}

test_libraryPrefix_with_exports() async {
addSource('/libA.dart', 'library libA; class A { }');
addSource('/libB.dart', 'library libB; export "/libA.dart"; class B { }');
Expand Down Expand Up @@ -91,6 +108,42 @@ class LibraryMemberContributorTest extends DartCompletionContributorTest {
assertNotSuggested('==');
}

test_PrefixedIdentifier_library_inPart() async {
// SimpleIdentifier PrefixedIdentifier ExpressionStatement
var libFile = '${testFile.substring(0, testFile.length - 5)}A.dart';
addSource(
'/testB.dart',
'''
lib B;
var T1;
class X { }
class Y { }''');
addSource(
libFile,
'''
library testA;
import "/testB.dart" as b;
part "$testFile";
var T2;
class A { }''');
addTestSource('''
part of testA;
main() {b.^}''');
// Assume that libraries containing has been computed for part files
await computeLibrariesContaining();
await computeSuggestions();
expect(replacementOffset, completionOffset);
expect(replacementLength, 0);
assertSuggestClass('X');
assertSuggestClass('Y');
assertSuggestTopLevelVar('T1', null);
assertNotSuggested('T2');
assertNotSuggested('Object');
assertNotSuggested('b');
assertNotSuggested('A');
assertNotSuggested('==');
}

test_PrefixedIdentifier_library_typesOnly() async {
// SimpleIdentifier PrefixedIdentifier TypeName
addSource(
Expand Down

0 comments on commit 830a37a

Please sign in to comment.