Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Side by Side Diff: pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart

Issue 2868383002: Recover from merge problem (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 import 'dart:math';
8 9
9 import 'package:analysis_server/protocol/protocol_generated.dart'; 10 import 'package:analysis_server/protocol/protocol_generated.dart';
10 import 'package:analysis_server/src/protocol_server.dart' hide Element; 11 import 'package:analysis_server/src/protocol_server.dart' hide Element;
11 import 'package:analysis_server/src/services/correction/source_buffer.dart'; 12 import 'package:analysis_server/src/services/correction/source_buffer.dart';
12 import 'package:analysis_server/src/services/correction/util.dart'; 13 import 'package:analysis_server/src/services/correction/util.dart';
13 import 'package:analyzer/dart/ast/ast.dart'; 14 import 'package:analyzer/dart/ast/ast.dart';
14 import 'package:analyzer/dart/ast/token.dart'; 15 import 'package:analyzer/dart/ast/token.dart';
15 import 'package:analyzer/dart/element/element.dart'; 16 import 'package:analyzer/dart/element/element.dart';
16 import 'package:analyzer/error/error.dart'; 17 import 'package:analyzer/error/error.dart';
17 import 'package:analyzer/error/error.dart' as engine; 18 import 'package:analyzer/error/error.dart' as engine;
18 import 'package:analyzer/src/dart/ast/utilities.dart'; 19 import 'package:analyzer/src/dart/ast/utilities.dart';
19 import 'package:analyzer/src/dart/error/hint_codes.dart'; 20 import 'package:analyzer/src/dart/error/hint_codes.dart';
20 import 'package:analyzer/src/dart/error/syntactic_errors.dart'; 21 import 'package:analyzer/src/dart/error/syntactic_errors.dart';
22 import 'package:analyzer/src/error/codes.dart';
21 import 'package:analyzer/src/generated/engine.dart'; 23 import 'package:analyzer/src/generated/engine.dart';
22 import 'package:analyzer/src/generated/java_core.dart'; 24 import 'package:analyzer/src/generated/java_core.dart';
23 import 'package:analyzer/src/generated/source.dart'; 25 import 'package:analyzer/src/generated/source.dart';
24 import 'package:analyzer_plugin/utilities/range_factory.dart'; 26 import 'package:analyzer_plugin/utilities/range_factory.dart';
25 27
26 /** 28 /**
27 * An enumeration of possible statement completion kinds. 29 * An enumeration of possible statement completion kinds.
28 */ 30 */
29 class DartStatementCompletion { 31 class DartStatementCompletion {
30 static const NO_COMPLETION = 32 static const NO_COMPLETION =
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
194 } 196 }
195 for (engine.AnalysisError error in statementContext.errors) { 197 for (engine.AnalysisError error in statementContext.errors) {
196 if (error.offset >= node.offset && 198 if (error.offset >= node.offset &&
197 error.offset <= node.offset + node.length) { 199 error.offset <= node.offset + node.length) {
198 if (error.errorCode is! HintCode) { 200 if (error.errorCode is! HintCode) {
199 errors.add(error); 201 errors.add(error);
200 } 202 }
201 } 203 }
202 } 204 }
203 205
206 _checkExpressions();
204 if (node is Statement) { 207 if (node is Statement) {
205 if (errors.isEmpty) { 208 if (errors.isEmpty) {
206 if (_complete_ifStatement() || 209 if (_complete_ifStatement() ||
207 _complete_forStatement() || 210 _complete_forStatement() ||
208 _complete_forEachStatement() || 211 _complete_forEachStatement() ||
209 _complete_whileStatement() || 212 _complete_whileStatement() ||
210 _complete_controlFlowBlock()) { 213 _complete_controlFlowBlock()) {
211 return completion; 214 return completion;
212 } 215 }
213 } else { 216 } else {
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 } 281 }
279 282
280 String _baseNodeText(AstNode astNode) { 283 String _baseNodeText(AstNode astNode) {
281 String text = utils.getNodeText(astNode); 284 String text = utils.getNodeText(astNode);
282 if (text.endsWith(eol)) { 285 if (text.endsWith(eol)) {
283 text = text.substring(0, text.length - eol.length); 286 text = text.substring(0, text.length - eol.length);
284 } 287 }
285 return text; 288 return text;
286 } 289 }
287 290
291 void _checkExpressions() {
292 // Note: This may queue edits that have to be accounted for later.
293 // See _lengthOfInsertions().
294 AstNode errorMatching(errorCode, {partialMatch = null}) {
295 var error = _findError(errorCode, partialMatch: partialMatch);
296 if (error == null) {
297 return null;
298 }
299 AstNode expr = _selectedNode();
300 return (expr.getAncestor((n) => n is StringInterpolation) == null)
301 ? expr
302 : null;
303 }
304
305 var expr = errorMatching(ScannerErrorCode.UNTERMINATED_STRING_LITERAL);
306 if (expr != null) {
307 String source = utils.getNodeText(expr);
308 String content = source;
309 int char = content.codeUnitAt(0);
310 if (char == 'r'.codeUnitAt(0)) {
311 content = source.substring(1);
312 char = content.codeUnitAt(0);
313 }
314 String delimiter;
315 int loc;
316 if (content.length >= 3 &&
317 char == content.codeUnitAt(1) &&
318 char == content.codeUnitAt(2)) {
319 // multi-line string
320 delimiter = content.substring(0, 3);
321 int newlineLoc = source.indexOf(eol, selectionOffset - expr.offset);
322 if (newlineLoc < 0) {
323 newlineLoc = source.length;
324 }
325 loc = newlineLoc + expr.offset;
326 } else {
327 // add first char of src
328 delimiter = content.substring(0, 1);
329 loc = expr.offset + source.length;
330 }
331 _removeError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL);
332 _addInsertEdit(loc, delimiter);
333 }
334 expr = errorMatching(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "']'");
335 if (expr != null) {
336 expr = expr.getAncestor((n) => n is ListLiteral);
337 if (expr != null) {
338 ListLiteral lit = expr;
339 if (lit.rightBracket.isSynthetic) {
340 String src = utils.getNodeText(expr).trim();
341 int loc = expr.offset + src.length;
342 if (src.contains(eol)) {
343 String indent = utils.getNodePrefix(node);
344 _addInsertEdit(loc, ',' + eol + indent + ']');
345 } else {
346 _addInsertEdit(loc, ']');
347 }
348 _removeError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "']'");
349 var ms =
350 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
351 if (ms != null) {
352 // Ensure the semicolon gets inserted in the correct location.
353 ms.offset = loc - 1;
354 }
355 }
356 }
357 }
358 // The following code is similar to the code for ']' but does not work well.
359 // A closing brace is recognized as belong to the map even if it is intended
360 // to close a block of code.
361 /*
362 expr = errorMatching(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "'}'");
363 if (expr != null) {
364 expr = expr.getAncestor((n) => n is MapLiteral);
365 if (expr != null) {
366 MapLiteral lit = expr;
367 String src = utils.getNodeText(expr).trim();
368 int loc = expr.offset + src.length;
369 if (lit.entries.last.separator.isSynthetic) {
370 _addInsertEdit(loc, ': ');
371 }
372 if (!src.endsWith('}')/*lit.rightBracket.isSynthetic*/) {
373 _addInsertEdit(loc, '}');
374 }
375 _removeError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "'}'");
376 var ms =
377 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
378 if (ms != null) {
379 // Ensure the semicolon gets inserted in the correct location.
380 ms.offset = loc - 1;
381 }
382 }
383 }
384 */
385 }
386
288 bool _complete_classDeclaration() { 387 bool _complete_classDeclaration() {
289 if (node is! ClassDeclaration) { 388 if (node is! ClassDeclaration) {
290 return false; 389 return false;
291 } 390 }
292 ClassDeclaration decl = node; 391 ClassDeclaration decl = node;
293 if (decl.leftBracket.isSynthetic && errors.length == 1) { 392 if (decl.leftBracket.isSynthetic && errors.length == 1) {
294 // The space before the left brace is assumed to exist, even if it does no t. 393 // The space before the left brace is assumed to exist, even if it does no t.
295 SourceBuilder sb = new SourceBuilder(file, decl.end - 1); 394 SourceBuilder sb = new SourceBuilder(file, decl.end - 1);
296 sb.append(' '); 395 sb.append(' ');
297 _appendEmptyBraces(sb, true); 396 _appendEmptyBraces(sb, true);
(...skipping 17 matching lines...) Expand all
315 return false; 414 return false;
316 } 415 }
317 AstNode outer = node.parent.parent; 416 AstNode outer = node.parent.parent;
318 if (!(outer is DoStatement || 417 if (!(outer is DoStatement ||
319 outer is ForStatement || 418 outer is ForStatement ||
320 outer is ForEachStatement || 419 outer is ForEachStatement ||
321 outer is IfStatement || 420 outer is IfStatement ||
322 outer is WhileStatement)) { 421 outer is WhileStatement)) {
323 return false; 422 return false;
324 } 423 }
424 int previousInsertions = _lengthOfInsertions();
325 int delta = 0; 425 int delta = 0;
326 if (errors.isNotEmpty) { 426 if (errors.isNotEmpty) {
327 var error = 427 var error =
328 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'"); 428 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
329 if (error != null) { 429 if (error != null) {
330 int insertOffset; 430 int insertOffset;
331 if (expr == null || expr.isSynthetic) { 431 if (expr == null || expr.isSynthetic) {
332 if (node is ReturnStatement) { 432 if (node is ReturnStatement) {
333 insertOffset = (node as ReturnStatement).returnKeyword.end; 433 insertOffset = (node as ReturnStatement).returnKeyword.end;
334 } else if (node is ExpressionStatement) { 434 } else if (node is ExpressionStatement) {
335 insertOffset = 435 insertOffset =
336 ((node as ExpressionStatement).expression as ThrowExpression) 436 ((node as ExpressionStatement).expression as ThrowExpression)
337 .throwKeyword 437 .throwKeyword
338 .end; 438 .end;
339 } else { 439 } else {
340 insertOffset = node.end; // Not reached. 440 insertOffset = node.end; // Not reached.
341 } 441 }
342 } else { 442 } else {
343 insertOffset = expr.end; 443 insertOffset = expr.end;
344 } 444 }
345 //TODO(messick) Uncomment the following line when error location is fixe d. 445 //TODO(messick) Uncomment the following line when error location is fixe d.
346 //insertOffset = error.offset + error.length; 446 //insertOffset = error.offset + error.length;
347 _addInsertEdit(insertOffset, ';'); 447 _addInsertEdit(insertOffset, ';');
348 delta = 1; 448 delta = 1;
349 } 449 }
350 } 450 }
351 int offset = _appendNewlinePlusIndentAt(node.parent.end); 451 int offset = _appendNewlinePlusIndentAt(node.parent.end);
352 exitPosition = new Position(file, offset + delta); 452 exitPosition = new Position(file, offset + delta + previousInsertions);
353 _setCompletion(DartStatementCompletion.COMPLETE_CONTROL_FLOW_BLOCK); 453 _setCompletion(DartStatementCompletion.COMPLETE_CONTROL_FLOW_BLOCK);
354 return true; 454 return true;
355 } 455 }
356 456
357 bool _complete_doStatement() { 457 bool _complete_doStatement() {
358 if (node is! DoStatement) { 458 if (node is! DoStatement) {
359 return false; 459 return false;
360 } 460 }
361 DoStatement statement = node; 461 DoStatement statement = node;
362 SourceBuilder sb = _sourceBuilderAfterKeyword(statement.doKeyword); 462 SourceBuilder sb = _sourceBuilderAfterKeyword(statement.doKeyword);
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 _setCompletion(DartStatementCompletion.COMPLETE_FOR_EACH_STMT); 579 _setCompletion(DartStatementCompletion.COMPLETE_FOR_EACH_STMT);
480 return true; 580 return true;
481 } 581 }
482 582
483 bool _complete_forStatement() { 583 bool _complete_forStatement() {
484 if (node is! ForStatement) { 584 if (node is! ForStatement) {
485 return false; 585 return false;
486 } 586 }
487 ForStatement forNode = node; 587 ForStatement forNode = node;
488 SourceBuilder sb; 588 SourceBuilder sb;
489 int delta = 0; 589 int replacementLength = 0;
490 if (forNode.leftParenthesis.isSynthetic) { 590 if (forNode.leftParenthesis.isSynthetic) {
491 if (!forNode.rightParenthesis.isSynthetic) { 591 if (!forNode.rightParenthesis.isSynthetic) {
492 return false; 592 return false;
493 } 593 }
494 // keywordOnly (unit test name suffix that exercises this branch) 594 // keywordOnly (unit test name suffix that exercises this branch)
495 sb = _sourceBuilderAfterKeyword(forNode.forKeyword); 595 sb = _sourceBuilderAfterKeyword(forNode.forKeyword);
496 sb.append('('); 596 sb.append('(');
497 sb.setExitOffset(); 597 sb.setExitOffset();
498 sb.append(')'); 598 sb.append(')');
499 } else { 599 } else {
500 if (!forNode.rightSeparator.isSynthetic) { 600 if (!forNode.rightSeparator.isSynthetic) {
501 // Fully-defined init, cond, updaters so nothing more needed here. 601 // Fully-defined init, cond, updaters so nothing more needed here.
502 // emptyParts 602 // emptyParts, noError
503 sb = new SourceBuilder(file, forNode.rightParenthesis.offset + 1); 603 sb = new SourceBuilder(file, forNode.rightParenthesis.offset + 1);
504 } else if (!forNode.leftSeparator.isSynthetic) { 604 } else if (!forNode.leftSeparator.isSynthetic) {
505 if (_isSyntheticExpression(forNode.condition)) { 605 if (_isSyntheticExpression(forNode.condition)) {
506 exitPosition = _newPosition(forNode.leftSeparator.offset + 1);
507 String text = utils 606 String text = utils
508 .getNodeText(forNode) 607 .getNodeText(forNode)
509 .substring(forNode.leftSeparator.offset - forNode.offset); 608 .substring(forNode.leftSeparator.offset - forNode.offset);
510 if (text.startsWith(new RegExp(r';\s*\)'))) { 609 Match match =
511 // emptyCondition 610 new RegExp(r';\s*(/\*.*\*/\s*)?\)[ \t]*').matchAsPrefix(text);
512 int end = text.indexOf(')'); 611 if (match != null) {
612 // emptyCondition, emptyInitializersEmptyCondition
613 replacementLength = match.end - match.start;
513 sb = new SourceBuilder(file, forNode.leftSeparator.offset); 614 sb = new SourceBuilder(file, forNode.leftSeparator.offset);
514 _addReplaceEdit(new SourceRange(sb.offset, end), '; ; '); 615 sb.append('; ${match.group(1) == null ? '' : match.group(1)}; )');
515 delta = end - '; '.length; 616 String suffix = text.substring(match.end);
617 if (suffix.trim().isNotEmpty) {
618 sb.append(' ');
619 sb.append(suffix.trim());
620 replacementLength += suffix.length;
621 if (suffix.endsWith(eol)) {
622 // emptyCondition
623 replacementLength -= eol.length;
624 }
625 }
626 exitPosition = _newPosition(forNode.leftSeparator.offset + 2);
516 } else { 627 } else {
517 // emptyInitializersEmptyCondition 628 return false; // Line comment in condition
518 exitPosition = _newPosition(forNode.rightParenthesis.offset);
519 sb = new SourceBuilder(file, forNode.rightParenthesis.offset);
520 } 629 }
521 } else { 630 } else {
522 // emptyUpdaters 631 // emptyUpdaters
523 exitPosition = _newPosition(forNode.rightSeparator.offset); 632 sb = new SourceBuilder(file, forNode.rightParenthesis.offset);
524 sb = new SourceBuilder(file, forNode.rightSeparator.offset); 633 replacementLength = 1;
525 _addReplaceEdit(new SourceRange(sb.offset, 0), '; '); 634 sb.append('; )');
526 delta = -'; '.length; 635 exitPosition = _newPosition(forNode.rightSeparator.offset + 2);
527 } 636 }
528 } else if (_isSyntheticExpression(forNode.initialization)) { 637 } else if (_isSyntheticExpression(forNode.initialization)) {
529 // emptyInitializers 638 // emptyInitializers
530 exitPosition = _newPosition(forNode.rightParenthesis.offset); 639 exitPosition = _newPosition(forNode.rightParenthesis.offset);
531 sb = new SourceBuilder(file, forNode.rightParenthesis.offset); 640 sb = new SourceBuilder(file, forNode.rightParenthesis.offset);
532 } else { 641 } else {
533 int start = forNode.condition.offset + forNode.condition.length; 642 int start = forNode.condition.offset + forNode.condition.length;
534 String text = 643 String text =
535 utils.getNodeText(forNode).substring(start - forNode.offset); 644 utils.getNodeText(forNode).substring(start - forNode.offset);
536 if (text.startsWith(new RegExp(r'\s*\)'))) { 645 if (text.startsWith(new RegExp(r'\s*\)'))) {
537 // missingLeftSeparator 646 // missingLeftSeparator
538 int end = text.indexOf(')'); 647 int end = text.indexOf(')');
539 sb = new SourceBuilder(file, start); 648 sb = new SourceBuilder(file, start);
540 _addReplaceEdit(new SourceRange(start, end), '; '); 649 _addReplaceEdit(new SourceRange(start, end), '; ; ');
541 delta = end - '; '.length; 650 exitPosition = new Position(file, start - (end - '; '.length));
542 exitPosition = new Position(file, start);
543 } else { 651 } else {
544 // Not possible; any comment following init is attached to init. 652 // Not possible; any comment following init is attached to init.
545 exitPosition = _newPosition(forNode.rightParenthesis.offset); 653 exitPosition = _newPosition(forNode.rightParenthesis.offset);
546 sb = new SourceBuilder(file, forNode.rightParenthesis.offset); 654 sb = new SourceBuilder(file, forNode.rightParenthesis.offset);
547 } 655 }
548 } 656 }
549 } 657 }
550 if (!_statementHasValidBody(forNode.forKeyword, forNode.body)) { 658 if (!_statementHasValidBody(forNode.forKeyword, forNode.body)) {
551 // keywordOnly, noError 659 // keywordOnly, noError
552 sb.append(' '); 660 sb.append(' ');
553 _appendEmptyBraces(sb, exitPosition == null); 661 _appendEmptyBraces(sb, true /*exitPosition == null*/);
662 } else if (forNode.body is Block) {
663 Block body = forNode.body;
664 if (body.rightBracket.end <= selectionOffset) {
665 // emptyInitializersAfterBody
666 errors = []; // Ignore errors; they are for previous statement.
667 return false; // If cursor is after closing brace just add newline.
668 }
554 } 669 }
555 if (delta != 0 && exitPosition != null) { 670 _insertBuilder(sb, replacementLength);
556 // missingLeftSeparator
557 exitPosition = new Position(file, exitPosition.offset - delta);
558 }
559 _insertBuilder(sb);
560 _setCompletion(DartStatementCompletion.COMPLETE_FOR_STMT); 671 _setCompletion(DartStatementCompletion.COMPLETE_FOR_STMT);
561 return true; 672 return true;
562 } 673 }
563 674
564 bool _complete_functionDeclaration() { 675 bool _complete_functionDeclaration() {
565 if (node is! MethodDeclaration && node is! FunctionDeclaration) { 676 if (node is! MethodDeclaration && node is! FunctionDeclaration) {
566 return false; 677 return false;
567 } 678 }
568 SourceBuilder sb = new SourceBuilder(file, node.end - 1); 679 bool needsParen = false;
680 int computeExitPos(FormalParameterList parameters) {
681 if (needsParen = parameters.rightParenthesis.isSynthetic) {
682 var error = _findError(ParserErrorCode.MISSING_CLOSING_PARENTHESIS);
683 if (error != null) {
684 return error.offset - 1;
685 }
686 }
687 return node.end - 1;
688 }
689
690 int paramListEnd;
691 if (node is FunctionDeclaration) {
692 FunctionDeclaration func = node;
693 paramListEnd = computeExitPos(func.functionExpression.parameters);
694 } else {
695 MethodDeclaration meth = node;
696 paramListEnd = computeExitPos(meth.parameters);
697 }
698 SourceBuilder sb = new SourceBuilder(file, paramListEnd);
699 if (needsParen) {
700 sb.append(')');
701 }
569 sb.append(' '); 702 sb.append(' ');
570 _appendEmptyBraces(sb, true); 703 _appendEmptyBraces(sb, true);
571 _insertBuilder(sb); 704 _insertBuilder(sb);
572 _setCompletion(DartStatementCompletion.COMPLETE_FUNCTION_DECLARATION); 705 _setCompletion(DartStatementCompletion.COMPLETE_FUNCTION_DECLARATION);
573 return true; 706 return true;
574 } 707 }
575 708
576 bool _complete_functionDeclarationStatement() { 709 bool _complete_functionDeclarationStatement() {
577 if (node is! FunctionDeclarationStatement) { 710 if (node is! FunctionDeclarationStatement) {
578 return false; 711 return false;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
614 747
615 bool _complete_ifOrWhileStatement( 748 bool _complete_ifOrWhileStatement(
616 _KeywordConditionBlockStructure statement, StatementCompletionKind kind) { 749 _KeywordConditionBlockStructure statement, StatementCompletionKind kind) {
617 if (_statementHasValidBody(statement.keyword, statement.block)) { 750 if (_statementHasValidBody(statement.keyword, statement.block)) {
618 return false; 751 return false;
619 } 752 }
620 SourceBuilder sb = _complete_keywordCondition(statement); 753 SourceBuilder sb = _complete_keywordCondition(statement);
621 if (sb == null) { 754 if (sb == null) {
622 return false; 755 return false;
623 } 756 }
757 int overshoot = _lengthOfDeletions();
624 sb.append(' '); 758 sb.append(' ');
625 _appendEmptyBraces(sb, exitPosition == null); 759 _appendEmptyBraces(sb, exitPosition == null);
626 _insertBuilder(sb); 760 _insertBuilder(sb);
761 if (overshoot != 0) {
762 exitPosition = _newPosition(exitPosition.offset - overshoot);
763 }
627 _setCompletion(kind); 764 _setCompletion(kind);
628 return true; 765 return true;
629 } 766 }
630 767
631 bool _complete_ifStatement() { 768 bool _complete_ifStatement() {
632 if (node is! IfStatement) { 769 if (node is! IfStatement) {
633 return false; 770 return false;
634 } 771 }
635 IfStatement ifNode = node; 772 IfStatement ifNode = node;
636 if (ifNode.elseKeyword != null) { 773 if (ifNode.elseKeyword != null) {
774 if (selectionOffset >= ifNode.elseKeyword.end &&
775 ifNode.elseStatement is EmptyStatement) {
776 SourceBuilder sb = new SourceBuilder(file, selectionOffset);
777 String src = utils.getNodeText(ifNode);
778 if (!src
779 .substring(ifNode.elseKeyword.end - node.offset)
780 .startsWith(new RegExp(r'[ \t]'))) {
781 sb.append(' ');
782 }
783 _appendEmptyBraces(sb, true);
784 _insertBuilder(sb);
785 _setCompletion(DartStatementCompletion.COMPLETE_IF_STMT);
786 return true;
787 }
637 return false; 788 return false;
638 } 789 }
639 var stmt = new _KeywordConditionBlockStructure( 790 var stmt = new _KeywordConditionBlockStructure(
640 ifNode.ifKeyword, 791 ifNode.ifKeyword,
641 ifNode.leftParenthesis, 792 ifNode.leftParenthesis,
642 ifNode.condition, 793 ifNode.condition,
643 ifNode.rightParenthesis, 794 ifNode.rightParenthesis,
644 ifNode.thenStatement); 795 ifNode.thenStatement);
645 return _complete_ifOrWhileStatement( 796 return _complete_ifOrWhileStatement(
646 stmt, DartStatementCompletion.COMPLETE_IF_STMT); 797 stmt, DartStatementCompletion.COMPLETE_IF_STMT);
647 } 798 }
648 799
649 SourceBuilder _complete_keywordCondition( 800 SourceBuilder _complete_keywordCondition(
650 _KeywordConditionBlockStructure statement) { 801 _KeywordConditionBlockStructure statement) {
651 SourceBuilder sb; 802 SourceBuilder sb;
652 if (statement.leftParenthesis.isSynthetic) { 803 if (statement.leftParenthesis.isSynthetic) {
653 if (!statement.rightParenthesis.isSynthetic) { 804 if (!statement.rightParenthesis.isSynthetic) {
654 // Quite unlikely to see this so don't try to fix it. 805 // Quite unlikely to see this so don't try to fix it.
655 return null; 806 return null;
656 } 807 }
657 sb = _sourceBuilderAfterKeyword(statement.keyword); 808 sb = _sourceBuilderAfterKeyword(statement.keyword);
658 sb.append('('); 809 sb.append('(');
659 sb.setExitOffset(); 810 sb.setExitOffset();
660 sb.append(')'); 811 sb.append(')');
661 } else { 812 } else {
662 if (_isSyntheticExpression(statement.condition)) { 813 if (_isSyntheticExpression(statement.condition)) {
663 exitPosition = _newPosition(statement.leftParenthesis.offset + 1); 814 exitPosition = _newPosition(statement.leftParenthesis.offset + 1);
664 sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1); 815 sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1);
665 } else { 816 } else {
666 sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1); 817 int afterParen = statement.rightParenthesis.offset + 1;
818 if (utils
819 .getNodeText(node)
820 .substring(afterParen - node.offset)
821 .startsWith(new RegExp(r'[ \t]'))) {
822 _addReplaceEdit(new SourceRange(afterParen, 1), '');
823 sb = new SourceBuilder(file, afterParen + 1);
824 } else {
825 sb = new SourceBuilder(file, afterParen);
826 }
667 } 827 }
668 } 828 }
669 return sb; 829 return sb;
670 } 830 }
671 831
672 bool _complete_methodCall() { 832 bool _complete_methodCall() {
673 var parenError = 833 var parenError =
674 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "')'"); 834 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "')'");
675 if (parenError == null) { 835 if (parenError == null) {
676 return false; 836 return false;
677 } 837 }
678 AstNode argList = _selectedNode(at: parenError.offset - 1) 838 AstNode argList = _selectedNode(at: selectionOffset)
679 .getAncestor((n) => n is ArgumentList); 839 .getAncestor((n) => n is ArgumentList);
840 if (argList == null) {
841 argList = _selectedNode(at: parenError.offset)
842 .getAncestor((n) => n is ArgumentList);
843 }
680 if (argList?.getAncestor((n) => n == node) == null) { 844 if (argList?.getAncestor((n) => n == node) == null) {
681 return false; 845 return false;
682 } 846 }
683 int loc = argList.end - 1; 847 int previousInsertions = _lengthOfInsertions();
848 int loc = min(selectionOffset, argList.end - 1);
684 int delta = 1; 849 int delta = 1;
685 var semicolonError = 850 var semicolonError =
686 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'"); 851 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
687 if (semicolonError == null) { 852 if (semicolonError == null) {
688 loc += 1; 853 loc += 1;
689 delta = 0; 854 delta = 0;
690 } 855 }
691 _addInsertEdit(loc, ')'); 856 _addInsertEdit(loc, ')');
692 if (semicolonError != null) { 857 if (semicolonError != null) {
693 _addInsertEdit(loc, ';'); 858 _addInsertEdit(loc, ';');
694 } 859 }
695 String indent = utils.getLinePrefix(selectionOffset); 860 String indent = utils.getLinePrefix(selectionOffset);
696 int exit = utils.getLineNext(selectionOffset); 861 int exit = utils.getLineNext(selectionOffset);
697 _addInsertEdit(exit, indent + eol); 862 _addInsertEdit(exit, indent + eol);
698 exit += indent.length + eol.length; 863 exit += indent.length + eol.length + previousInsertions;
699 864
700 _setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, exit + delta); 865 _setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, exit + delta);
701 return true; 866 return true;
702 } 867 }
703 868
704 bool _complete_simpleEnter() { 869 bool _complete_simpleEnter() {
705 int offset; 870 int offset;
706 if (!errors.isEmpty) { 871 if (!errors.isEmpty) {
707 offset = selectionOffset; 872 offset = selectionOffset;
708 } else { 873 } else {
709 String indent = utils.getLinePrefix(selectionOffset); 874 String indent = utils.getLinePrefix(selectionOffset);
710 int loc = utils.getLineNext(selectionOffset); 875 int loc = utils.getLineNext(selectionOffset);
711 _addInsertEdit(loc, indent + eol); 876 _addInsertEdit(loc, indent + eol);
712 offset = loc + indent.length + eol.length; 877 offset = loc + indent.length;
713 } 878 }
714 _setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, offset); 879 _setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, offset);
715 return true; 880 return true;
716 } 881 }
717 882
718 bool _complete_simpleSemicolon() { 883 bool _complete_simpleSemicolon() {
719 if (errors.length != 1) { 884 if (errors.length != 1) {
720 return false; 885 return false;
721 } 886 }
722 var error = _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'"); 887 var error = _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
723 if (error != null) { 888 if (error != null) {
889 int previousInsertions = _lengthOfInsertions();
724 // TODO(messick) Fix this to find the correct place in all cases. 890 // TODO(messick) Fix this to find the correct place in all cases.
725 int insertOffset = error.offset + error.length; 891 int insertOffset = error.offset + error.length;
726 _addInsertEdit(insertOffset, ';'); 892 _addInsertEdit(insertOffset, ';');
727 int offset = _appendNewlinePlusIndent() + 1 /* ';' */; 893 int offset = _appendNewlinePlusIndent() + 1 /*';'*/ + previousInsertions;
728 _setCompletionAt(DartStatementCompletion.SIMPLE_SEMICOLON, offset); 894 _setCompletionAt(DartStatementCompletion.SIMPLE_SEMICOLON, offset);
729 return true; 895 return true;
730 } 896 }
731 return false; 897 return false;
732 } 898 }
733 899
734 bool _complete_switchStatement() { 900 bool _complete_switchStatement() {
735 if (node is! SwitchStatement) { 901 if (node is! SwitchStatement) {
736 return false; 902 return false;
737 } 903 }
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
801 // keywordOnly 967 // keywordOnly
802 sb = new SourceBuilder(file, tryNode.tryKeyword.end); 968 sb = new SourceBuilder(file, tryNode.tryKeyword.end);
803 sb.append(' '); 969 sb.append(' ');
804 } 970 }
805 _appendEmptyBraces(sb, true); 971 _appendEmptyBraces(sb, true);
806 _insertBuilder(sb); 972 _insertBuilder(sb);
807 sb = null; 973 sb = null;
808 } else if ((catchNode = _findInvalidElement(tryNode.catchClauses)) != 974 } else if ((catchNode = _findInvalidElement(tryNode.catchClauses)) !=
809 null) { 975 null) {
810 if (catchNode.onKeyword != null) { 976 if (catchNode.onKeyword != null) {
811 if (catchNode.exceptionType.length == 0) { 977 if (catchNode.exceptionType.length == 0 ||
978 null !=
979 _findError(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE,
980 partialMatch: "name 'catch")) {
812 String src = utils.getNodeText(catchNode); 981 String src = utils.getNodeText(catchNode);
813 if (src.startsWith(new RegExp(r'on[ \t]+'))) { 982 if (src.startsWith(new RegExp(r'on[ \t]+'))) {
814 if (src.startsWith(new RegExp(r'on[ \t][ \t]+'))) { 983 if (src.startsWith(new RegExp(r'on[ \t][ \t]+'))) {
815 // onSpaces 984 // onSpaces
816 exitPosition = new Position(file, catchNode.onKeyword.end + 1); 985 exitPosition = new Position(file, catchNode.onKeyword.end + 1);
817 sb = new SourceBuilder(file, catchNode.onKeyword.end + 2); 986 sb = new SourceBuilder(file, catchNode.onKeyword.end + 2);
818 addSpace = false; 987 addSpace = false;
819 } else { 988 } else {
820 // onSpace 989 // onSpace
821 sb = new SourceBuilder(file, catchNode.onKeyword.end + 1); 990 sb = new SourceBuilder(file, catchNode.onKeyword.end + 1);
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
962 return true; 1131 return true;
963 } 1132 }
964 AstNode p = n.parent; 1133 AstNode p = n.parent;
965 return p is! Statement && p?.parent is! Statement; 1134 return p is! Statement && p?.parent is! Statement;
966 } 1135 }
967 1136
968 bool _isSyntheticExpression(Expression expr) { 1137 bool _isSyntheticExpression(Expression expr) {
969 return expr is SimpleIdentifier && expr.isSynthetic; 1138 return expr is SimpleIdentifier && expr.isSynthetic;
970 } 1139 }
971 1140
1141 int _lengthOfDeletions() {
1142 if (change.edits.isEmpty) {
1143 return 0;
1144 }
1145 int length = 0;
1146 for (SourceFileEdit edit in change.edits) {
1147 for (SourceEdit srcEdit in edit.edits) {
1148 if (srcEdit.length > 0) {
1149 length += srcEdit.length - srcEdit.replacement.length;
1150 }
1151 }
1152 }
1153 return length;
1154 }
1155
1156 int _lengthOfInsertions() {
1157 // Any _complete_*() that may follow changes made by _checkExpressions()
1158 // must cache the result of this method and add that value to its
1159 // exit position. That's assuming all edits are done in increasing position.
1160 // There are currently no editing sequences that produce both insertions and
1161 // deletions, but if there were this approach would have to be generalized.
1162 if (change.edits.isEmpty) {
1163 return 0;
1164 }
1165 int length = 0;
1166 for (SourceFileEdit edit in change.edits) {
1167 for (SourceEdit srcEdit in edit.edits) {
1168 if (srcEdit.length == 0) {
1169 length += srcEdit.replacement.length;
1170 }
1171 }
1172 }
1173 return length;
1174 }
1175
972 Position _newPosition(int offset) { 1176 Position _newPosition(int offset) {
973 return new Position(file, offset); 1177 return new Position(file, offset);
974 } 1178 }
975 1179
1180 void _removeError(errorCode, {partialMatch = null}) {
1181 var error = _findError(errorCode, partialMatch: partialMatch);
1182 if (error != null) {
1183 errors.remove(error);
1184 }
1185 }
1186
976 AstNode _selectedNode({int at: null}) => 1187 AstNode _selectedNode({int at: null}) =>
977 new NodeLocator(at == null ? selectionOffset : at).searchWithin(unit); 1188 new NodeLocator(at == null ? selectionOffset : at).searchWithin(unit);
978 1189
979 void _setCompletion(StatementCompletionKind kind, [List args]) { 1190 void _setCompletion(StatementCompletionKind kind, [List args]) {
980 assert(exitPosition != null); 1191 assert(exitPosition != null);
981 change.selection = exitPosition; 1192 change.selection = exitPosition;
982 change.message = formatList(kind.message, args); 1193 change.message = formatList(kind.message, args);
983 linkedPositionGroups.values 1194 linkedPositionGroups.values
984 .forEach((group) => change.addLinkedEditGroup(group)); 1195 .forEach((group) => change.addLinkedEditGroup(group));
985 completion = new StatementCompletion(kind, change); 1196 completion = new StatementCompletion(kind, change);
986 } 1197 }
987 1198
988 void _setCompletionAt(StatementCompletionKind kind, int offset, [List args]) { 1199 void _setCompletionAt(StatementCompletionKind kind, int offset, [List args]) {
989 exitPosition = _newPosition(offset); 1200 exitPosition = _newPosition(offset);
990 _setCompletion(kind, args); 1201 _setCompletion(kind, args);
991 } 1202 }
992 1203
993 SourceBuilder _sourceBuilderAfterKeyword(Token keyword) { 1204 SourceBuilder _sourceBuilderAfterKeyword(Token keyword) {
994 SourceBuilder sb; 1205 SourceBuilder sb;
995 String text = _baseNodeText(node); 1206 String text = _baseNodeText(node);
996 text = text.substring(keyword.offset - node.offset); 1207 text = text.substring(keyword.offset - node.offset);
997 int len = keyword.length; 1208 int len = keyword.length;
998 if (text.length == len || 1209 if (text.length == len || // onCatchComment
999 !text.substring(len, len + 1).contains(new RegExp(r'\s'))) { 1210 !text.substring(len, len + 1).contains(new RegExp(r'[ \t]'))) {
1000 sb = new SourceBuilder(file, keyword.offset + len); 1211 sb = new SourceBuilder(file, keyword.offset + len);
1001 sb.append(' '); 1212 sb.append(' ');
1002 } else { 1213 } else {
1003 sb = new SourceBuilder(file, keyword.offset + len + 1); 1214 sb = new SourceBuilder(file, keyword.offset + len + 1);
1004 } 1215 }
1005 return sb; 1216 return sb;
1006 } 1217 }
1007 1218
1008 bool _statementHasValidBody(Token keyword, Statement body) { 1219 bool _statementHasValidBody(Token keyword, Statement body) {
1009 // A "valid" body is either a non-synthetic block or a single statement 1220 // A "valid" body is either a non-synthetic block or a single statement
(...skipping 15 matching lines...) Expand all
1025 final Token keyword; 1236 final Token keyword;
1026 final Token leftParenthesis, rightParenthesis; 1237 final Token leftParenthesis, rightParenthesis;
1027 final Expression condition; 1238 final Expression condition;
1028 final Statement block; 1239 final Statement block;
1029 1240
1030 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis, 1241 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis,
1031 this.condition, this.rightParenthesis, this.block); 1242 this.condition, this.rightParenthesis, this.block);
1032 1243
1033 int get offset => keyword.offset; 1244 int get offset => keyword.offset;
1034 } 1245 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698