Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library services.src.completion.statement; | 5 library services.src.completion.statement; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analysis_server/plugin/protocol/protocol.dart'; | 9 import 'package:analysis_server/plugin/protocol/protocol.dart'; |
| 10 import 'package:analysis_server/src/protocol_server.dart' hide Element; | 10 import 'package:analysis_server/src/protocol_server.dart' hide Element; |
| 11 import 'package:analysis_server/src/services/correction/source_buffer.dart'; | 11 import 'package:analysis_server/src/services/correction/source_buffer.dart'; |
| 12 import 'package:analysis_server/src/services/correction/source_range.dart'; | 12 import 'package:analysis_server/src/services/correction/source_range.dart'; |
| 13 import 'package:analysis_server/src/services/correction/util.dart'; | 13 import 'package:analysis_server/src/services/correction/util.dart'; |
| 14 import 'package:analyzer/dart/ast/ast.dart'; | 14 import 'package:analyzer/dart/ast/ast.dart'; |
| 15 import 'package:analyzer/dart/ast/token.dart'; | 15 import 'package:analyzer/dart/ast/token.dart'; |
| 16 import 'package:analyzer/dart/ast/visitor.dart'; | |
| 17 import 'package:analyzer/dart/element/element.dart'; | 16 import 'package:analyzer/dart/element/element.dart'; |
| 18 import 'package:analyzer/error/error.dart'; | 17 import 'package:analyzer/error/error.dart'; |
| 19 import 'package:analyzer/error/error.dart' as engine; | 18 import 'package:analyzer/error/error.dart' as engine; |
| 20 import 'package:analyzer/src/dart/ast/utilities.dart'; | 19 import 'package:analyzer/src/dart/ast/utilities.dart'; |
| 21 import 'package:analyzer/src/dart/error/hint_codes.dart'; | 20 import 'package:analyzer/src/dart/error/hint_codes.dart'; |
| 22 import 'package:analyzer/src/dart/error/syntactic_errors.dart'; | 21 import 'package:analyzer/src/dart/error/syntactic_errors.dart'; |
| 23 import 'package:analyzer/src/generated/engine.dart'; | 22 import 'package:analyzer/src/generated/engine.dart'; |
| 24 import 'package:analyzer/src/generated/java_core.dart'; | 23 import 'package:analyzer/src/generated/java_core.dart'; |
| 25 import 'package:analyzer/src/generated/source.dart'; | 24 import 'package:analyzer/src/generated/source.dart'; |
| 26 | 25 |
| 27 /** | 26 /** |
| 28 * An enumeration of possible statement completion kinds. | 27 * An enumeration of possible statement completion kinds. |
| 29 */ | 28 */ |
| 30 class DartStatementCompletion { | 29 class DartStatementCompletion { |
| 31 static const NO_COMPLETION = | 30 static const NO_COMPLETION = |
| 32 const StatementCompletionKind('No_COMPLETION', 'No completion available'); | 31 const StatementCompletionKind('No_COMPLETION', 'No completion available'); |
| 33 static const SIMPLE_ENTER = const StatementCompletionKind( | 32 static const SIMPLE_ENTER = const StatementCompletionKind( |
| 34 'SIMPLE_ENTER', "Insert a newline at the end of the current line"); | 33 'SIMPLE_ENTER', "Insert a newline at the end of the current line"); |
| 35 static const SIMPLE_SEMICOLON = const StatementCompletionKind( | 34 static const SIMPLE_SEMICOLON = const StatementCompletionKind( |
| 36 'SIMPLE_SEMICOLON', "Add a semicolon and newline"); | 35 'SIMPLE_SEMICOLON', "Add a semicolon and newline"); |
| 36 static const COMPLETE_CONTROL_FLOW_BLOCK = const StatementCompletionKind( | |
| 37 'COMPLETE_CONTROL_FLOW_BLOCK', "Complete control flow block"); | |
| 37 static const COMPLETE_DO_STMT = const StatementCompletionKind( | 38 static const COMPLETE_DO_STMT = const StatementCompletionKind( |
| 38 'COMPLETE_DO_STMT', "Complete do-statement"); | 39 'COMPLETE_DO_STMT', "Complete do-statement"); |
| 39 static const COMPLETE_IF_STMT = const StatementCompletionKind( | 40 static const COMPLETE_IF_STMT = const StatementCompletionKind( |
| 40 'COMPLETE_IF_STMT', "Complete if-statement"); | 41 'COMPLETE_IF_STMT', "Complete if-statement"); |
| 41 static const COMPLETE_FOR_STMT = const StatementCompletionKind( | 42 static const COMPLETE_FOR_STMT = const StatementCompletionKind( |
| 42 'COMPLETE_FOR_STMT', "Complete for-statement"); | 43 'COMPLETE_FOR_STMT', "Complete for-statement"); |
| 43 static const COMPLETE_FOR_EACH_STMT = const StatementCompletionKind( | 44 static const COMPLETE_FOR_EACH_STMT = const StatementCompletionKind( |
| 44 'COMPLETE_FOR_EACH_STMT', "Complete for-each-statement"); | 45 'COMPLETE_FOR_EACH_STMT', "Complete for-each-statement"); |
| 45 static const COMPLETE_SWITCH_STMT = const StatementCompletionKind( | 46 static const COMPLETE_SWITCH_STMT = const StatementCompletionKind( |
| 46 'COMPLETE_SWITCH_STMT', "Complete switch-statement"); | 47 'COMPLETE_SWITCH_STMT', "Complete switch-statement"); |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 179 for (engine.AnalysisError error in statementContext.errors) { | 180 for (engine.AnalysisError error in statementContext.errors) { |
| 180 if (error.offset >= node.offset && | 181 if (error.offset >= node.offset && |
| 181 error.offset <= node.offset + node.length) { | 182 error.offset <= node.offset + node.length) { |
| 182 if (error.errorCode is! HintCode) { | 183 if (error.errorCode is! HintCode) { |
| 183 errors.add(error); | 184 errors.add(error); |
| 184 } | 185 } |
| 185 } | 186 } |
| 186 } | 187 } |
| 187 | 188 |
| 188 if (errors.isEmpty) { | 189 if (errors.isEmpty) { |
| 189 if (_complete_simpleEnter()) { | 190 if (_complete_controlFlowBlock() || _complete_simpleEnter()) { |
| 190 return completion; | 191 return completion; |
| 191 } | 192 } |
| 192 } else { | 193 } else { |
| 193 if (_complete_ifStatement() || | 194 if (_complete_ifStatement() || |
| 194 _complete_doStatement() || | 195 _complete_doStatement() || |
| 195 _complete_forStatement() || | 196 _complete_forStatement() || |
| 196 _complete_forEachStatement() || | 197 _complete_forEachStatement() || |
| 197 _complete_switchStatement() || | 198 _complete_switchStatement() || |
| 198 _complete_tryStatement() || | 199 _complete_tryStatement() || |
| 199 _complete_whileStatement() || | 200 _complete_whileStatement() || |
| 201 _complete_controlFlowBlock() || | |
| 200 _complete_simpleSemicolon() || | 202 _complete_simpleSemicolon() || |
| 201 _complete_controlFlowBlock() || | |
| 202 _complete_simpleEnter()) { | 203 _complete_simpleEnter()) { |
| 203 return completion; | 204 return completion; |
| 204 } | 205 } |
| 205 } | 206 } |
| 206 return NO_COMPLETION; | 207 return NO_COMPLETION; |
| 207 } | 208 } |
| 208 | 209 |
| 209 void _addInsertEdit(int offset, String text) { | 210 void _addInsertEdit(int offset, String text) { |
| 210 SourceEdit edit = new SourceEdit(offset, 0, text); | 211 SourceEdit edit = new SourceEdit(offset, 0, text); |
| 211 doSourceChange_addElementEdit(change, unitElement, edit); | 212 doSourceChange_addElementEdit(change, unitElement, edit); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 224 sb.append(utils.getIndent(1)); | 225 sb.append(utils.getIndent(1)); |
| 225 if (needsExitMark && sb.exitOffset == null) { | 226 if (needsExitMark && sb.exitOffset == null) { |
| 226 sb.setExitOffset(); | 227 sb.setExitOffset(); |
| 227 } | 228 } |
| 228 sb.append(eol); | 229 sb.append(eol); |
| 229 sb.append(indent); | 230 sb.append(indent); |
| 230 sb.append('}'); | 231 sb.append('}'); |
| 231 } | 232 } |
| 232 | 233 |
| 233 int _appendNewlinePlusIndent() { | 234 int _appendNewlinePlusIndent() { |
| 235 return _appendNewlinePlusIndentAt(selectionOffset); | |
| 236 } | |
| 237 | |
| 238 int _appendNewlinePlusIndentAt(int offset) { | |
|
Brian Wilkerson
2017/04/21 21:45:45
I don't feel strongly about it, but in case you di
| |
| 234 // Append a newline plus proper indent and another newline. | 239 // Append a newline plus proper indent and another newline. |
| 235 // Return the position before the second newline. | 240 // Return the position before the second newline. |
| 236 String indent = utils.getLinePrefix(selectionOffset); | 241 String indent = utils.getLinePrefix(offset); |
| 237 int loc = utils.getLineNext(selectionOffset); | 242 int loc = utils.getLineNext(offset); |
| 238 _addInsertEdit(loc, indent + eol); | 243 _addInsertEdit(loc, indent + eol); |
| 239 return loc + indent.length; | 244 return loc + indent.length; |
| 240 } | 245 } |
| 241 | 246 |
| 242 String _baseNodeText(AstNode astNode) { | 247 String _baseNodeText(AstNode astNode) { |
| 243 String text = utils.getNodeText(astNode); | 248 String text = utils.getNodeText(astNode); |
| 244 if (text.endsWith(eol)) { | 249 if (text.endsWith(eol)) { |
| 245 text = text.substring(0, text.length - eol.length); | 250 text = text.substring(0, text.length - eol.length); |
| 246 } | 251 } |
| 247 return text; | 252 return text; |
| 248 } | 253 } |
| 249 | 254 |
| 250 bool _complete_controlFlowBlock() { | 255 bool _complete_controlFlowBlock() { |
| 251 //TODO(messick) Implement _complete_controlFlowBlock | 256 Expression expr = (node is ExpressionStatement) |
| 252 // Use statement completion to move the cursor to a new line outside the | 257 ? (node as ExpressionStatement).expression |
| 253 // current block. The statement has no errors in this case. Used to jump | 258 : (node is ReturnStatement |
| 254 // out of do/for/if/while blocks. | 259 ? (node as ReturnStatement).expression |
| 255 return false; | 260 : null); |
| 261 if (!(node is ReturnStatement || expr is ThrowExpression)) { | |
| 262 return false; | |
| 263 } | |
| 264 if (node.parent is! Block) { | |
| 265 return false; | |
| 266 } | |
| 267 AstNode outer = node.parent.parent; | |
| 268 if (!(outer is DoStatement || | |
| 269 outer is ForStatement || | |
| 270 outer is ForEachStatement || | |
| 271 outer is IfStatement || | |
| 272 outer is WhileStatement)) { | |
| 273 return false; | |
| 274 } | |
| 275 int delta = 0; | |
| 276 if (errors.isNotEmpty) { | |
| 277 var error = | |
| 278 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'"); | |
| 279 if (error != null) { | |
| 280 int insertOffset; | |
| 281 if (expr == null || expr.isSynthetic) { | |
| 282 if (node is ReturnStatement) { | |
| 283 insertOffset = (node as ReturnStatement).returnKeyword.end; | |
| 284 } else if (node is ExpressionStatement) { | |
| 285 insertOffset = | |
| 286 ((node as ExpressionStatement).expression as ThrowExpression) | |
| 287 .throwKeyword | |
| 288 .end; | |
| 289 } else { | |
| 290 insertOffset = node.end; // Not reached. | |
| 291 } | |
| 292 } else { | |
| 293 insertOffset = expr.end; | |
| 294 } | |
| 295 //TODO(messick) Uncomment the following line when error location is fixe d. | |
| 296 //insertOffset = error.offset + error.length; | |
| 297 _addInsertEdit(insertOffset, ';'); | |
| 298 delta = 1; | |
| 299 } | |
| 300 } | |
| 301 int offset = _appendNewlinePlusIndentAt(node.parent.end); | |
| 302 exitPosition = new Position(file, offset + delta); | |
| 303 _setCompletion(DartStatementCompletion.COMPLETE_CONTROL_FLOW_BLOCK); | |
| 304 return true; | |
| 256 } | 305 } |
| 257 | 306 |
| 258 bool _complete_doStatement() { | 307 bool _complete_doStatement() { |
| 259 if (node is! DoStatement) { | 308 if (node is! DoStatement) { |
| 260 return false; | 309 return false; |
| 261 } | 310 } |
| 262 DoStatement statement = node; | 311 DoStatement statement = node; |
| 263 SourceBuilder sb = _sourceBuilderAfterKeyword(statement.doKeyword); | 312 SourceBuilder sb = _sourceBuilderAfterKeyword(statement.doKeyword); |
| 264 bool hasWhileKeyword = statement.whileKeyword.lexeme == "while"; | 313 bool hasWhileKeyword = statement.whileKeyword.lexeme == "while"; |
| 265 int exitDelta = 0; | 314 int exitDelta = 0; |
| (...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 533 _setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, offset); | 582 _setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, offset); |
| 534 return true; | 583 return true; |
| 535 } | 584 } |
| 536 | 585 |
| 537 bool _complete_simpleSemicolon() { | 586 bool _complete_simpleSemicolon() { |
| 538 if (errors.length != 1) { | 587 if (errors.length != 1) { |
| 539 return false; | 588 return false; |
| 540 } | 589 } |
| 541 var error = _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'"); | 590 var error = _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'"); |
| 542 if (error != null) { | 591 if (error != null) { |
| 592 // TODO(messick) Fix this to find the correct place in all cases. | |
| 543 int insertOffset = error.offset + error.length; | 593 int insertOffset = error.offset + error.length; |
| 544 _addInsertEdit(insertOffset, ';'); | 594 _addInsertEdit(insertOffset, ';'); |
| 545 int offset = _appendNewlinePlusIndent() + 1 /* ';' */; | 595 int offset = _appendNewlinePlusIndent() + 1 /* ';' */; |
| 546 _setCompletionAt(DartStatementCompletion.SIMPLE_SEMICOLON, offset); | 596 _setCompletionAt(DartStatementCompletion.SIMPLE_SEMICOLON, offset); |
| 547 return true; | 597 return true; |
| 548 } | 598 } |
| 549 return false; | 599 return false; |
| 550 } | 600 } |
| 551 | 601 |
| 552 bool _complete_switchStatement() { | 602 bool _complete_switchStatement() { |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 806 final Token keyword; | 856 final Token keyword; |
| 807 final Token leftParenthesis, rightParenthesis; | 857 final Token leftParenthesis, rightParenthesis; |
| 808 final Expression condition; | 858 final Expression condition; |
| 809 final Statement block; | 859 final Statement block; |
| 810 | 860 |
| 811 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis, | 861 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis, |
| 812 this.condition, this.rightParenthesis, this.block); | 862 this.condition, this.rightParenthesis, this.block); |
| 813 | 863 |
| 814 int get offset => keyword.offset; | 864 int get offset => keyword.offset; |
| 815 } | 865 } |
| OLD | NEW |