| 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; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 36 static const COMPLETE_DO_STMT = const StatementCompletionKind( | 36 static const COMPLETE_DO_STMT = const StatementCompletionKind( |
| 37 'COMPLETE_DO_STMT', "Complete do-statement"); | 37 'COMPLETE_DO_STMT', "Complete do-statement"); |
| 38 static const COMPLETE_IF_STMT = const StatementCompletionKind( | 38 static const COMPLETE_IF_STMT = const StatementCompletionKind( |
| 39 'COMPLETE_IF_STMT', "Complete if-statement"); | 39 'COMPLETE_IF_STMT', "Complete if-statement"); |
| 40 static const COMPLETE_FOR_STMT = const StatementCompletionKind( | 40 static const COMPLETE_FOR_STMT = const StatementCompletionKind( |
| 41 'COMPLETE_FOR_STMT', "Complete for-statement"); | 41 'COMPLETE_FOR_STMT', "Complete for-statement"); |
| 42 static const COMPLETE_FOR_EACH_STMT = const StatementCompletionKind( | 42 static const COMPLETE_FOR_EACH_STMT = const StatementCompletionKind( |
| 43 'COMPLETE_FOR_EACH_STMT', "Complete for-each-statement"); | 43 'COMPLETE_FOR_EACH_STMT', "Complete for-each-statement"); |
| 44 static const COMPLETE_SWITCH_STMT = const StatementCompletionKind( | 44 static const COMPLETE_SWITCH_STMT = const StatementCompletionKind( |
| 45 'COMPLETE_SWITCH_STMT', "Complete switch-statement"); | 45 'COMPLETE_SWITCH_STMT', "Complete switch-statement"); |
| 46 static const COMPLETE_TRY_STMT = const StatementCompletionKind( |
| 47 'COMPLETE_TRY_STMT', "Complete try-statement"); |
| 46 static const COMPLETE_WHILE_STMT = const StatementCompletionKind( | 48 static const COMPLETE_WHILE_STMT = const StatementCompletionKind( |
| 47 'COMPLETE_WHILE_STMT', "Complete while-statement"); | 49 'COMPLETE_WHILE_STMT', "Complete while-statement"); |
| 48 } | 50 } |
| 49 | 51 |
| 50 /** | 52 /** |
| 51 * A description of a statement completion. | 53 * A description of a statement completion. |
| 52 * | 54 * |
| 53 * Clients may not extend, implement or mix-in this class. | 55 * Clients may not extend, implement or mix-in this class. |
| 54 */ | 56 */ |
| 55 class StatementCompletion { | 57 class StatementCompletion { |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 | 186 |
| 185 // TODO(messick) Consider changing (some of) this to a visitor. | 187 // TODO(messick) Consider changing (some of) this to a visitor. |
| 186 if (_complete_ifStatement() || | 188 if (_complete_ifStatement() || |
| 187 _complete_doStatement() || | 189 _complete_doStatement() || |
| 188 _complete_forStatement() || | 190 _complete_forStatement() || |
| 189 _complete_forEachStatement() || | 191 _complete_forEachStatement() || |
| 190 _complete_switchStatement() || | 192 _complete_switchStatement() || |
| 191 _complete_tryStatement() || | 193 _complete_tryStatement() || |
| 192 _complete_whileStatement() || | 194 _complete_whileStatement() || |
| 193 _complete_simpleSemicolon() || | 195 _complete_simpleSemicolon() || |
| 196 _complete_controlFlowBlock() || |
| 194 _complete_simpleEnter()) { | 197 _complete_simpleEnter()) { |
| 195 return completion; | 198 return completion; |
| 196 } | 199 } |
| 197 return NO_COMPLETION; | 200 return NO_COMPLETION; |
| 198 } | 201 } |
| 199 | 202 |
| 200 void _addInsertEdit(int offset, String text) { | 203 void _addInsertEdit(int offset, String text) { |
| 201 SourceEdit edit = new SourceEdit(offset, 0, text); | 204 SourceEdit edit = new SourceEdit(offset, 0, text); |
| 202 doSourceChange_addElementEdit(change, unitElement, edit); | 205 doSourceChange_addElementEdit(change, unitElement, edit); |
| 203 } | 206 } |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 284 } | 287 } |
| 285 SourceBuilder sb2; | 288 SourceBuilder sb2; |
| 286 if (hasWhileKeyword) { | 289 if (hasWhileKeyword) { |
| 287 var stmt = new _KeywordConditionBlockStructure( | 290 var stmt = new _KeywordConditionBlockStructure( |
| 288 statement.whileKeyword, | 291 statement.whileKeyword, |
| 289 statement.leftParenthesis, | 292 statement.leftParenthesis, |
| 290 statement.condition, | 293 statement.condition, |
| 291 statement.rightParenthesis, | 294 statement.rightParenthesis, |
| 292 null); | 295 null); |
| 293 sb2 = _complete_keywordCondition(stmt); | 296 sb2 = _complete_keywordCondition(stmt); |
| 297 if (sb2 == null) { |
| 298 return false; |
| 299 } |
| 294 if (sb2.length == 0) { | 300 if (sb2.length == 0) { |
| 295 // true if condition is '()' | 301 // true if condition is '()' |
| 296 if (exitPosition != null) { | 302 if (exitPosition != null) { |
| 297 if (statement.semicolon.isSynthetic) { | 303 if (statement.semicolon.isSynthetic) { |
| 298 _insertBuilder(sb); | 304 _insertBuilder(sb); |
| 299 sb = new SourceBuilder(file, exitPosition.offset + 1); | 305 sb = new SourceBuilder(file, exitPosition.offset + 1); |
| 300 sb.append(';'); | 306 sb.append(';'); |
| 301 } | 307 } |
| 302 } | 308 } |
| 303 } else { | 309 } else { |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 447 exitPosition = new Position(file, exitPosition.offset - delta); | 453 exitPosition = new Position(file, exitPosition.offset - delta); |
| 448 } | 454 } |
| 449 _insertBuilder(sb); | 455 _insertBuilder(sb); |
| 450 _setCompletion(DartStatementCompletion.COMPLETE_FOR_STMT); | 456 _setCompletion(DartStatementCompletion.COMPLETE_FOR_STMT); |
| 451 return true; | 457 return true; |
| 452 } | 458 } |
| 453 | 459 |
| 454 bool _complete_ifOrWhileStatement( | 460 bool _complete_ifOrWhileStatement( |
| 455 _KeywordConditionBlockStructure statement, StatementCompletionKind kind) { | 461 _KeywordConditionBlockStructure statement, StatementCompletionKind kind) { |
| 456 SourceBuilder sb = _complete_keywordCondition(statement); | 462 SourceBuilder sb = _complete_keywordCondition(statement); |
| 463 if (sb == null) { |
| 464 return false; |
| 465 } |
| 457 if (statement.block is EmptyStatement) { | 466 if (statement.block is EmptyStatement) { |
| 458 sb.append(' '); | 467 sb.append(' '); |
| 459 _appendEmptyBraces(sb, exitPosition == null); | 468 _appendEmptyBraces(sb, exitPosition == null); |
| 460 } | 469 } |
| 461 _insertBuilder(sb); | 470 _insertBuilder(sb); |
| 462 _setCompletion(kind); | 471 _setCompletion(kind); |
| 463 return true; | 472 return true; |
| 464 } | 473 } |
| 465 | 474 |
| 466 bool _complete_ifStatement() { | 475 bool _complete_ifStatement() { |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 570 // See https://github.com/dart-lang/sdk/issues/29391 | 579 // See https://github.com/dart-lang/sdk/issues/29391 |
| 571 sb.append(' '); | 580 sb.append(' '); |
| 572 _appendEmptyBraces(sb, exitPosition == null); | 581 _appendEmptyBraces(sb, exitPosition == null); |
| 573 } | 582 } |
| 574 _insertBuilder(sb); | 583 _insertBuilder(sb); |
| 575 _setCompletion(DartStatementCompletion.COMPLETE_SWITCH_STMT); | 584 _setCompletion(DartStatementCompletion.COMPLETE_SWITCH_STMT); |
| 576 return true; | 585 return true; |
| 577 } | 586 } |
| 578 | 587 |
| 579 bool _complete_tryStatement() { | 588 bool _complete_tryStatement() { |
| 580 // TODO(messick) Implement _complete_tryStatement | 589 if (errors.isEmpty || node is! TryStatement) { |
| 581 return false; | 590 return false; |
| 591 } |
| 592 TryStatement tryNode = node; |
| 593 SourceBuilder sb; |
| 594 CatchClause catchNode; |
| 595 bool addSpace = true; |
| 596 if (tryNode.body.leftBracket.isSynthetic) { |
| 597 String src = utils.getNodeText(tryNode); |
| 598 if (src |
| 599 .substring(tryNode.tryKeyword.end - tryNode.offset) |
| 600 .startsWith(new RegExp(r'[ \t]+'))) { |
| 601 // keywordSpace |
| 602 sb = new SourceBuilder(file, tryNode.tryKeyword.end + 1); |
| 603 } else { |
| 604 // keywordOnly |
| 605 sb = new SourceBuilder(file, tryNode.tryKeyword.end); |
| 606 sb.append(' '); |
| 607 } |
| 608 _appendEmptyBraces(sb, true); |
| 609 _insertBuilder(sb); |
| 610 sb = null; |
| 611 } else if ((catchNode = _firstInvalidCatch(tryNode.catchClauses)) != null) { |
| 612 if (catchNode.onKeyword != null) { |
| 613 if (catchNode.exceptionType.length == 0) { |
| 614 String src = utils.getNodeText(catchNode); |
| 615 if (src.startsWith(new RegExp(r'on[ \t]+'))) { |
| 616 if (src.startsWith(new RegExp(r'on[ \t][ \t]+'))) { |
| 617 // onSpaces |
| 618 exitPosition = new Position(file, catchNode.onKeyword.end + 1); |
| 619 sb = new SourceBuilder(file, catchNode.onKeyword.end + 2); |
| 620 addSpace = false; |
| 621 } else { |
| 622 // onSpace |
| 623 sb = new SourceBuilder(file, catchNode.onKeyword.end + 1); |
| 624 sb.setExitOffset(); |
| 625 } |
| 626 } else { |
| 627 // onOnly |
| 628 sb = new SourceBuilder(file, catchNode.onKeyword.end); |
| 629 sb.append(' '); |
| 630 sb.setExitOffset(); |
| 631 } |
| 632 } else { |
| 633 // onType |
| 634 sb = new SourceBuilder(file, catchNode.exceptionType.end); |
| 635 } |
| 636 } |
| 637 if (catchNode.catchKeyword != null) { |
| 638 // catchOnly |
| 639 var struct = new _KeywordConditionBlockStructure( |
| 640 catchNode.catchKeyword, |
| 641 catchNode.leftParenthesis, |
| 642 catchNode.exceptionParameter, |
| 643 catchNode.rightParenthesis, |
| 644 catchNode.body); |
| 645 if (sb != null) { |
| 646 // onCatch |
| 647 _insertBuilder(sb); |
| 648 } |
| 649 sb = _complete_keywordCondition(struct); |
| 650 if (sb == null) { |
| 651 return false; |
| 652 } |
| 653 } |
| 654 if (catchNode.body.leftBracket.isSynthetic) { |
| 655 // onOnly and others |
| 656 if (addSpace) { |
| 657 sb.append(' '); |
| 658 } |
| 659 _appendEmptyBraces(sb, exitPosition == null); |
| 660 } |
| 661 _insertBuilder(sb); |
| 662 } else if (tryNode.finallyKeyword != null) { |
| 663 if (tryNode.finallyBlock.leftBracket.isSynthetic) { |
| 664 // finallyOnly |
| 665 sb = new SourceBuilder(file, tryNode.finallyKeyword.end); |
| 666 sb.append(' '); |
| 667 _appendEmptyBraces(sb, true); |
| 668 _insertBuilder(sb); |
| 669 } |
| 670 } |
| 671 _setCompletion(DartStatementCompletion.COMPLETE_TRY_STMT); |
| 672 return true; |
| 582 } | 673 } |
| 583 | 674 |
| 584 bool _complete_whileStatement() { | 675 bool _complete_whileStatement() { |
| 585 if (errors.isEmpty || node is! WhileStatement) { | 676 if (errors.isEmpty || node is! WhileStatement) { |
| 586 return false; | 677 return false; |
| 587 } | 678 } |
| 588 WhileStatement whileNode = node; | 679 WhileStatement whileNode = node; |
| 589 if (whileNode != null) { | 680 if (whileNode != null) { |
| 590 var stmt = new _KeywordConditionBlockStructure( | 681 var stmt = new _KeywordConditionBlockStructure( |
| 591 whileNode.whileKeyword, | 682 whileNode.whileKeyword, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 604 errors.firstWhere((err) => err.errorCode == code, orElse: () => null); | 695 errors.firstWhere((err) => err.errorCode == code, orElse: () => null); |
| 605 if (error != null) { | 696 if (error != null) { |
| 606 if (partialMatch != null) { | 697 if (partialMatch != null) { |
| 607 return error.message.contains(partialMatch) ? error : null; | 698 return error.message.contains(partialMatch) ? error : null; |
| 608 } | 699 } |
| 609 return error; | 700 return error; |
| 610 } | 701 } |
| 611 return null; | 702 return null; |
| 612 } | 703 } |
| 613 | 704 |
| 705 CatchClause _firstInvalidCatch(NodeList<CatchClause> list) { |
| 706 return list.firstWhere((e) { |
| 707 bool found = false; |
| 708 for (var error in errors) { |
| 709 if (error.offset >= e.offset && error.offset <= e.end) { |
| 710 if (error.errorCode is! HintCode) { |
| 711 found = true; |
| 712 break; |
| 713 } |
| 714 } |
| 715 } |
| 716 return found; |
| 717 }, orElse: () => null); |
| 718 } |
| 719 |
| 614 LinkedEditGroup _getLinkedPosition(String groupId) { | 720 LinkedEditGroup _getLinkedPosition(String groupId) { |
| 615 LinkedEditGroup group = linkedPositionGroups[groupId]; | 721 LinkedEditGroup group = linkedPositionGroups[groupId]; |
| 616 if (group == null) { | 722 if (group == null) { |
| 617 group = new LinkedEditGroup.empty(); | 723 group = new LinkedEditGroup.empty(); |
| 618 linkedPositionGroups[groupId] = group; | 724 linkedPositionGroups[groupId] = group; |
| 619 } | 725 } |
| 620 return group; | 726 return group; |
| 621 } | 727 } |
| 622 | 728 |
| 623 void _insertBuilder(SourceBuilder builder, [int length = 0]) { | 729 void _insertBuilder(SourceBuilder builder, [int length = 0]) { |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 696 final Token keyword; | 802 final Token keyword; |
| 697 final Token leftParenthesis, rightParenthesis; | 803 final Token leftParenthesis, rightParenthesis; |
| 698 final Expression condition; | 804 final Expression condition; |
| 699 final Statement block; | 805 final Statement block; |
| 700 | 806 |
| 701 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis, | 807 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis, |
| 702 this.condition, this.rightParenthesis, this.block); | 808 this.condition, this.rightParenthesis, this.block); |
| 703 | 809 |
| 704 int get offset => keyword.offset; | 810 int get offset => keyword.offset; |
| 705 } | 811 } |
| OLD | NEW |