Index: pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart |
diff --git a/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart b/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart |
index da199fce78fa37a7b51bbadb54ae44f81a763d21..39d315390442719e6fbd34cb60ea8a8857218d8f 100644 |
--- a/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart |
+++ b/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart |
@@ -17,6 +17,7 @@ main() { |
defineReflectiveTests(_DeclarationCompletionTest); |
defineReflectiveTests(_ControlFlowCompletionTest); |
defineReflectiveTests(_DoCompletionTest); |
+ defineReflectiveTests(_ExpressionCompletionTest); |
defineReflectiveTests(_ForCompletionTest); |
defineReflectiveTests(_ForEachCompletionTest); |
defineReflectiveTests(_IfCompletionTest); |
@@ -303,6 +304,23 @@ class Sample { |
(s) => _afterLast(s, ' ')); |
} |
+ test_extendsNoBody() async { |
+ await _prepareCompletion( |
+ 'Sample', |
+ ''' |
+class Sample extends Object |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete class declaration', |
+ ''' |
+class Sample extends Object { |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
test_functionDeclNoBody() async { |
await _prepareCompletion( |
'source()', |
@@ -320,6 +338,42 @@ String source() { |
(s) => _after(s, ' ')); |
} |
+ test_functionDeclNoParen() async { |
+ await _prepareCompletion( |
+ 'source(', |
+ ''' |
+String source( |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete function declaration', |
+ ''' |
+String source() { |
+ //// |
+} |
+''', |
+ (s) => _after(s, ' ')); |
+ } |
+ |
+ test_implementsNoBody() async { |
+ await _prepareCompletion( |
+ 'Sample', |
+ ''' |
+class Interface {} |
+class Sample implements Interface |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete class declaration', |
+ ''' |
+class Interface {} |
+class Sample implements Interface { |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
test_methodDeclNoBody() async { |
await _prepareCompletion( |
'source()', |
@@ -341,6 +395,27 @@ class Sample { |
(s) => _after(s, ' ')); |
} |
+ test_methodDeclNoParen() async { |
+ await _prepareCompletion( |
+ 'source(', |
+ ''' |
+class Sample { |
+ String source( |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete function declaration', |
+ ''' |
+class Sample { |
+ String source() { |
+ //// |
+ } |
+} |
+''', |
+ (s) => _after(s, ' ')); |
+ } |
+ |
test_variableDeclNoBody() async { |
await _prepareCompletion( |
'source', |
@@ -356,6 +431,25 @@ String source; |
''', |
(s) => _after(s, ';\n')); |
} |
+ |
+ test_withNoBody() async { |
+ await _prepareCompletion( |
+ 'Sample', |
+ ''' |
+class M {} |
+class Sample extends Object with M |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete class declaration', |
+ ''' |
+class M {} |
+class Sample extends Object with M { |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
} |
@reflectiveTest |
@@ -491,10 +585,248 @@ main() { |
} |
@reflectiveTest |
+class _ExpressionCompletionTest extends StatementCompletionTest { |
+ test_listAssign() async { |
+ await _prepareCompletion( |
+ '= ', |
+ ''' |
+main() { |
+ var x = [1, 2, 3 |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Add a semicolon and newline', |
+ ''' |
+main() { |
+ var x = [1, 2, 3]; |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
+ test_listAssignMultiLine() async { |
+ // The indent of the final line is incorrect. |
+ await _prepareCompletion( |
+ '3', |
+ ''' |
+main() { |
+ var x = [ |
+ 1, |
+ 2, |
+ 3 |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Add a semicolon and newline', |
+ ''' |
+main() { |
+ var x = [ |
+ 1, |
+ 2, |
+ 3, |
+ ]; |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
+ @failingTest |
+ test_mapAssign() async { |
+ await _prepareCompletion( |
+ '3: 3', |
+ ''' |
+main() { |
+ var x = {1: 1, 2: 2, 3: 3 |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Add a semicolon and newline', |
+ ''' |
+main() { |
+ var x = {1: 1, 2: 2, 3: 3}; |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
+ @failingTest |
+ test_mapAssignMissingColon() async { |
+ await _prepareCompletion( |
+ '3', |
+ ''' |
+main() { |
+ var x = {1: 1, 2: 2, 3 |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Add a semicolon and newline', |
+ ''' |
+main() { |
+ var x = {1: 1, 2: 2, 3: }; |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
+ test_returnString() async { |
+ await _prepareCompletion( |
+ 'text', |
+ ''' |
+main() { |
+ if (done()) { |
+ return 'text |
+ } |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete control flow block', |
+ ''' |
+main() { |
+ if (done()) { |
+ return 'text'; |
+ } |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
+ test_stringAssign() async { |
+ await _prepareCompletion( |
+ '= ', |
+ ''' |
+main() { |
+ var x = ' |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Add a semicolon and newline', |
+ ''' |
+main() { |
+ var x = ''; |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
+ test_stringSingle() async { |
+ await _prepareCompletion( |
+ 'text', |
+ ''' |
+main() { |
+ print("text |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Insert a newline at the end of the current line', |
+ ''' |
+main() { |
+ print("text"); |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
+ test_stringSingleRaw() async { |
+ await _prepareCompletion( |
+ 'text', |
+ ''' |
+main() { |
+ print(r"text |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Insert a newline at the end of the current line', |
+ ''' |
+main() { |
+ print(r"text"); |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
+ test_stringTriple() async { |
+ await _prepareCompletion( |
+ 'text', |
+ ''' |
+main() { |
+ print(\'\'\'text |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Insert a newline at the end of the current line', |
+ ''' |
+main() { |
+ print(\'\'\'text\'\'\'); |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
+ } |
+ |
+ test_stringTripleRaw() async { |
+ await _prepareCompletion( |
+ 'text', |
+ r""" |
+main() { |
+ print(r'''text |
+} |
+""", |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Insert a newline at the end of the current line', |
+ r""" |
+main() { |
+ print(r'''text'''); |
+ //// |
+} |
+""", |
+ (s) => _afterLast(s, ' ')); |
+ } |
+} |
+ |
+@reflectiveTest |
class _ForCompletionTest extends StatementCompletionTest { |
test_emptyCondition() async { |
await _prepareCompletion( |
- '}', |
+ '0;', |
+ ''' |
+main() { |
+ for (int i = 0;) /**/ //// |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete for-statement', |
+ ''' |
+main() { |
+ for (int i = 0; ; ) /**/ { |
+ //// |
+ } |
+} |
+''', |
+ (s) => _after(s, ' ')); |
+ } |
+ |
+ test_emptyConditionWithBody() async { |
+ await _prepareCompletion( |
+ '0;', |
''' |
main() { |
for (int i = 0;) { |
@@ -514,9 +846,9 @@ main() { |
} |
test_emptyInitializers() async { |
- // TODO(messick) This should insert a newline and move the cursor there. |
+ // This does nothing, same as for Java. |
await _prepareCompletion( |
- '}', |
+ 'r (', |
''' |
main() { |
for () { |
@@ -532,16 +864,37 @@ main() { |
} |
} |
''', |
- (s) => _after(s, 'for (')); |
+ (s) => _after(s, 'r (')); |
} |
- test_emptyInitializersEmptyCondition() async { |
+ test_emptyInitializersAfterBody() async { |
await _prepareCompletion( |
'}', |
''' |
main() { |
- for (;/**/) { |
+ for () { |
+ } |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Insert a newline at the end of the current line', |
+ ''' |
+main() { |
+ for () { |
+ } |
+ //// |
+} |
+''', |
+ (s) => _afterLast(s, ' ')); |
} |
+ |
+ test_emptyInitializersEmptyCondition() async { |
+ await _prepareCompletion( |
+ '/**/', |
+ ''' |
+main() { |
+ for (;/**/) |
} |
''', |
atEnd: true); |
@@ -549,11 +902,12 @@ main() { |
'Complete for-statement', |
''' |
main() { |
- for (;/**/) { |
+ for (; /**/; ) { |
+ //// |
} |
} |
''', |
- (s) => _after(s, '/**/')); |
+ (s) => _after(s, ' ')); |
} |
test_emptyParts() async { |
@@ -579,7 +933,28 @@ main() { |
test_emptyUpdaters() async { |
await _prepareCompletion( |
- '}', |
+ '/**/', |
+ ''' |
+main() { |
+ for (int i = 0; i < 10 /**/) |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete for-statement', |
+ ''' |
+main() { |
+ for (int i = 0; i < 10 /**/; ) { |
+ //// |
+ } |
+} |
+''', |
+ (s) => _after(s, ' ')); |
+ } |
+ |
+ test_emptyUpdatersWithBody() async { |
+ await _prepareCompletion( |
+ '/**/', |
''' |
main() { |
for (int i = 0; i < 10 /**/) { |
@@ -595,7 +970,7 @@ main() { |
} |
} |
''', |
- (s) => _after(s, '10 /**/; ')); |
+ (s) => _after(s, '*/; ')); |
} |
test_keywordOnly() async { |
@@ -621,7 +996,7 @@ main() { |
test_missingLeftSeparator() async { |
await _prepareCompletion( |
- '}', |
+ '= 0', |
''' |
main() { |
for (int i = 0) { |
@@ -633,7 +1008,7 @@ main() { |
'Complete for-statement', |
''' |
main() { |
- for (int i = 0; ) { |
+ for (int i = 0; ; ) { |
} |
} |
''', |
@@ -755,8 +1130,7 @@ main() { |
@reflectiveTest |
class _IfCompletionTest extends StatementCompletionTest { |
- test_afterCondition_BAD() async { |
- // TODO(messick) Stop inserting the space after the closing brace. |
+ test_afterCondition() async { |
await _prepareCompletion( |
'if (true) ', // Trigger completion after space. |
''' |
@@ -771,7 +1145,7 @@ main() { |
main() { |
if (true) { |
//// |
- } //// |
+ } |
} |
''', |
(s) => _after(s, ' ')); |
@@ -863,6 +1237,29 @@ main() { |
(s) => _after(s, ' ')); |
} |
+ test_withElse() async { |
+ await _prepareCompletion( |
+ 'else', |
+ ''' |
+main() { |
+ if () { |
+ } else |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete if-statement', |
+ ''' |
+main() { |
+ if () { |
+ } else { |
+ //// |
+ } |
+} |
+''', |
+ (s) => _after(s, ' ')); |
+ } |
+ |
test_withElse_BAD() async { |
await _prepareCompletion( |
'if ()', |
@@ -885,6 +1282,29 @@ main() { |
(s) => _after(s, 'if ()')); |
} |
+ test_withElseNoThen() async { |
+ await _prepareCompletion( |
+ 'else', |
+ ''' |
+main() { |
+ if () |
+ else |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete if-statement', |
+ ''' |
+main() { |
+ if () |
+ else { |
+ //// |
+ } |
+} |
+''', |
+ (s) => _after(s, ' ')); |
+ } |
+ |
test_withinEmptyCondition() async { |
await _prepareCompletion( |
'if (', |
@@ -1333,9 +1753,6 @@ main() { |
} |
''', |
atEnd: true); |
- // It would be better to expect the cursor to follow the on-keyword but |
- // the parser thinks the exception type is 'catch' so it's kinda broken. |
- // See https://github.com/dart-lang/sdk/issues/29410 |
_assertHasChange( |
'Complete try-statement', |
''' |
@@ -1349,6 +1766,31 @@ main() { |
(s) => _after(s, 'catch (')); |
} |
+ test_onCatchComment() async { |
+ await _prepareCompletion( |
+ 'on', |
+ ''' |
+main() { |
+ try { |
+ } on catch |
+ // |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete try-statement', |
+ ''' |
+main() { |
+ try { |
+ } on catch () { |
+ //// |
+ } |
+ // |
+} |
+''', |
+ (s) => _after(s, 'catch (')); |
+ } |
+ |
test_onOnly() async { |
await _prepareCompletion( |
'on', |