Index: pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart |
diff --git a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart |
index 13c35c1c2dc23374a7dd95d80cd49c2f6dad872e..0f4d004f956d6881132885da60f46ea7f86a95f6 100644 |
--- a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart |
+++ b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart |
@@ -37,6 +37,8 @@ class DartStatementCompletion { |
'COMPLETE_DO_STMT', "Complete do-statement"); |
static const COMPLETE_IF_STMT = const StatementCompletionKind( |
'COMPLETE_IF_STMT', "Complete if-statement"); |
+ static const COMPLETE_FOR_STMT = const StatementCompletionKind( |
+ 'COMPLETE_FOR_STMT', "Complete for-statement"); |
static const COMPLETE_WHILE_STMT = const StatementCompletionKind( |
'COMPLETE_WHILE_STMT', "Complete while-statement"); |
} |
@@ -179,6 +181,10 @@ class StatementCompletionProcessor { |
// TODO(messick) Consider changing (some of) this to a visitor. |
if (_complete_ifStatement() || |
_complete_doStatement() || |
+ _complete_forStatement() || |
+ _complete_forEachStatement() || |
+ _complete_switchStatement() || |
+ _complete_tryStatement() || |
_complete_whileStatement() || |
_complete_simpleSemicolon() || |
_complete_simpleEnter()) { |
@@ -187,11 +193,6 @@ class StatementCompletionProcessor { |
return NO_COMPLETION; |
} |
- void _addIndentEdit(SourceRange range, String oldIndent, String newIndent) { |
- SourceEdit edit = utils.createIndentEdit(range, oldIndent, newIndent); |
- doSourceChange_addElementEdit(change, unitElement, edit); |
- } |
- |
void _addInsertEdit(int offset, String text) { |
SourceEdit edit = new SourceEdit(offset, 0, text); |
doSourceChange_addElementEdit(change, unitElement, edit); |
@@ -238,12 +239,6 @@ class StatementCompletionProcessor { |
return false; |
} |
DoStatement statement = node; |
- var stmt = new _DoIfWhileStructure( |
- statement.whileKeyword, |
- statement.leftParenthesis, |
- statement.condition, |
- statement.rightParenthesis, |
- null); |
SourceBuilder sb = _sourceBuilderAfterKeyword(statement.doKeyword); |
bool hasWhileKeyword = statement.whileKeyword.lexeme == "while"; |
int exitDelta = 0; |
@@ -266,8 +261,8 @@ class StatementCompletionProcessor { |
sb = new SourceBuilder(file, sb.offset + delta); |
sb.append(' '); |
} |
- _appendEmptyBraces( |
- sb, !(hasWhileKeyword && _isEmptyExpression(statement.condition))); |
+ _appendEmptyBraces(sb, |
+ !(hasWhileKeyword && _isSyntheticExpression(statement.condition))); |
if (delta != 0) { |
exitDelta = sb.length - delta; |
} |
@@ -276,11 +271,17 @@ class StatementCompletionProcessor { |
} |
SourceBuilder sb2; |
if (hasWhileKeyword) { |
+ var stmt = new _KeywordConditionBlockStructure( |
+ statement.whileKeyword, |
+ statement.leftParenthesis, |
+ statement.condition, |
+ statement.rightParenthesis, |
+ null); |
sb2 = _complete_keywordCondition(stmt); |
if (sb2.length == 0) { |
// true if condition is '()' |
if (exitPosition != null) { |
- if (statement.semicolon.lexeme.isEmpty) { |
+ if (statement.semicolon.isSynthetic) { |
_insertBuilder(sb); |
sb = new SourceBuilder(file, exitPosition.offset + 1); |
sb.append(';'); |
@@ -309,8 +310,95 @@ class StatementCompletionProcessor { |
return true; |
} |
+ bool _complete_forEachStatement() { |
+ // TODO(messick) Implement _complete_forEachStatement |
+ return false; |
+ } |
+ |
+ bool _complete_forStatement() { |
+ if (errors.isEmpty || node is! ForStatement) { |
+ return false; |
+ } |
+ ForStatement forNode = node; |
+ SourceBuilder sb; |
+ int delta = 0; |
+ if (forNode.leftParenthesis.isSynthetic) { |
+ if (!forNode.rightParenthesis.isSynthetic) { |
+ return false; |
+ } |
+ // keywordOnly (unit test name suffix that exercises this branch) |
+ sb = _sourceBuilderAfterKeyword(forNode.forKeyword); |
+ sb.append('('); |
+ sb.setExitOffset(); |
+ sb.append(')'); |
+ } else { |
+ if (!forNode.rightSeparator.isSynthetic) { |
+ // Fully-defined init, cond, updaters so nothing more needed here. |
+ // emptyParts |
+ sb = new SourceBuilder(file, forNode.rightParenthesis.offset + 1); |
+ } else if (!forNode.leftSeparator.isSynthetic) { |
+ if (_isSyntheticExpression(forNode.condition)) { |
+ exitPosition = _newPosition(forNode.leftSeparator.offset + 1); |
+ String text = utils |
+ .getNodeText(forNode) |
+ .substring(forNode.leftSeparator.offset - forNode.offset); |
+ if (text.startsWith(new RegExp(r';\s*\)'))) { |
+ // emptyCondition |
+ int end = text.indexOf(')'); |
+ sb = new SourceBuilder(file, forNode.leftSeparator.offset); |
+ // TODO(messick) Consider adding two semicolons here. |
+ _addReplaceEdit(rangeStartLength(sb.offset, end), '; '); |
+ delta = end - '; '.length; |
+ } else { |
+ // emptyInitializersEmptyCondition |
+ exitPosition = _newPosition(forNode.rightParenthesis.offset); |
+ sb = new SourceBuilder(file, forNode.rightParenthesis.offset); |
+ } |
+ } else { |
+ // emptyUpdaters |
+ exitPosition = _newPosition(forNode.rightSeparator.offset); |
+ sb = new SourceBuilder(file, forNode.rightSeparator.offset); |
+ _addReplaceEdit(rangeStartLength(sb.offset, 0), '; '); |
+ delta = -'; '.length; |
+ } |
+ } else if (_isSyntheticExpression(forNode.initialization)) { |
+ // emptyInitializers |
+ exitPosition = _newPosition(forNode.rightParenthesis.offset); |
+ sb = new SourceBuilder(file, forNode.rightParenthesis.offset); |
+ } else { |
+ int start = forNode.condition.offset + forNode.condition.length; |
+ String text = |
+ utils.getNodeText(forNode).substring(start - forNode.offset); |
+ if (text.startsWith(new RegExp(r'\s*\)'))) { |
+ // missingLeftSeparator |
+ int end = text.indexOf(')'); |
+ sb = new SourceBuilder(file, start); |
+ _addReplaceEdit(rangeStartLength(start, end), '; '); |
+ delta = end - '; '.length; |
+ exitPosition = new Position(file, start); |
+ } else { |
+ // Not possible; any comment following init is attached to init. |
+ exitPosition = _newPosition(forNode.rightParenthesis.offset); |
+ sb = new SourceBuilder(file, forNode.rightParenthesis.offset); |
+ } |
+ } |
+ } |
+ if (forNode.body is EmptyStatement) { |
+ // keywordOnly |
+ sb.append(' '); |
+ _appendEmptyBraces(sb, exitPosition == null); |
+ } |
+ if (delta != 0 && exitPosition != null) { |
+ // missingLeftSeparator |
+ exitPosition = new Position(file, exitPosition.offset - delta); |
+ } |
+ _insertBuilder(sb); |
+ _setCompletion(DartStatementCompletion.COMPLETE_FOR_STMT); |
+ return true; |
+ } |
+ |
bool _complete_ifOrWhileStatement( |
- _DoIfWhileStructure statement, StatementCompletionKind kind) { |
+ _KeywordConditionBlockStructure statement, StatementCompletionKind kind) { |
SourceBuilder sb = _complete_keywordCondition(statement); |
if (statement.block is EmptyStatement) { |
sb.append(' '); |
@@ -330,7 +418,7 @@ class StatementCompletionProcessor { |
if (ifNode.elseKeyword != null) { |
return false; |
} |
- var stmt = new _DoIfWhileStructure( |
+ var stmt = new _KeywordConditionBlockStructure( |
ifNode.ifKeyword, |
ifNode.leftParenthesis, |
ifNode.condition, |
@@ -342,11 +430,11 @@ class StatementCompletionProcessor { |
return false; |
} |
- SourceBuilder _complete_keywordCondition(_DoIfWhileStructure statement) { |
+ SourceBuilder _complete_keywordCondition( |
+ _KeywordConditionBlockStructure statement) { |
SourceBuilder sb; |
- String text = _baseNodeText(node); |
- if (statement.leftParenthesis.lexeme.isEmpty) { |
- if (!statement.rightParenthesis.lexeme.isEmpty) { |
+ if (statement.leftParenthesis.isSynthetic) { |
+ if (!statement.rightParenthesis.isSynthetic) { |
// Quite unlikely to see this so don't try to fix it. |
return null; |
} |
@@ -355,7 +443,7 @@ class StatementCompletionProcessor { |
sb.setExitOffset(); |
sb.append(')'); |
} else { |
- if (_isEmptyExpression(statement.condition)) { |
+ if (_isSyntheticExpression(statement.condition)) { |
exitPosition = _newPosition(statement.leftParenthesis.offset + 1); |
sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1); |
} else { |
@@ -394,13 +482,23 @@ class StatementCompletionProcessor { |
return false; |
} |
+ bool _complete_switchStatement() { |
+ // TODO(messick) Implement _complete_switchStatement |
+ return false; |
+ } |
+ |
+ bool _complete_tryStatement() { |
+ // TODO(messick) Implement _complete_tryStatement |
+ return false; |
+ } |
+ |
bool _complete_whileStatement() { |
if (errors.isEmpty || node is! WhileStatement) { |
return false; |
} |
WhileStatement whileNode = node; |
if (whileNode != null) { |
- var stmt = new _DoIfWhileStructure( |
+ var stmt = new _KeywordConditionBlockStructure( |
whileNode.whileKeyword, |
whileNode.leftParenthesis, |
whileNode.condition, |
@@ -462,12 +560,8 @@ class StatementCompletionProcessor { |
return stmt is Block && stmt.statements.isEmpty; |
} |
- bool _isEmptyExpression(Expression expr) { |
- if (expr is! SimpleIdentifier) { |
- return false; |
- } |
- SimpleIdentifier id = expr as SimpleIdentifier; |
- return id.length == 0; |
+ bool _isSyntheticExpression(Expression expr) { |
+ return expr is SimpleIdentifier && expr.isSynthetic; |
} |
bool _isEmptyStatement(AstNode stmt) { |
@@ -509,14 +603,14 @@ class StatementCompletionProcessor { |
} |
// Encapsulate common structure of if-statement and while-statement. |
-class _DoIfWhileStructure { |
+class _KeywordConditionBlockStructure { |
final Token keyword; |
final Token leftParenthesis, rightParenthesis; |
final Expression condition; |
final Statement block; |
- _DoIfWhileStructure(this.keyword, this.leftParenthesis, this.condition, |
- this.rightParenthesis, this.block); |
+ _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis, |
+ this.condition, this.rightParenthesis, this.block); |
int get offset => keyword.offset; |
} |