Chromium Code Reviews| 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..6bc0e449f4c31f810a223eb4e9ad4e52f4e92290 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; |
| @@ -276,6 +271,12 @@ 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 '()' |
| @@ -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.lexeme.isEmpty) { |
|
scheglov
2017/04/18 21:52:09
It might be more straightforward to use forNode.le
messick
2017/04/18 22:16:24
Done.
|
| + if (!forNode.rightParenthesis.lexeme.isEmpty) { |
| + 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.lexeme.isNotEmpty) { |
| + // Fully-defined init, cond, updaters so nothing more needed here. |
| + // emptyParts |
| + sb = new SourceBuilder(file, forNode.rightParenthesis.offset + 1); |
| + } else if (forNode.leftSeparator.lexeme.isNotEmpty) { |
| + if (_isEmptyExpression(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 (_isEmptyExpression(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,9 +430,9 @@ 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) { |
| // Quite unlikely to see this so don't try to fix it. |
| @@ -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, |
| @@ -509,14 +607,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; |
| } |