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

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

Issue 2852073002: Complete declarations; detect invalid blocks (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 8
9 import 'package:analysis_server/protocol/protocol_generated.dart'; 9 import 'package:analysis_server/protocol/protocol_generated.dart';
10 import 'package:analysis_server/src/protocol_server.dart' hide Element; 10 import 'package:analysis_server/src/protocol_server.dart' hide Element;
(...skipping 15 matching lines...) Expand all
26 /** 26 /**
27 * An enumeration of possible statement completion kinds. 27 * An enumeration of possible statement completion kinds.
28 */ 28 */
29 class DartStatementCompletion { 29 class DartStatementCompletion {
30 static const NO_COMPLETION = 30 static const NO_COMPLETION =
31 const StatementCompletionKind('No_COMPLETION', 'No completion available'); 31 const StatementCompletionKind('No_COMPLETION', 'No completion available');
32 static const SIMPLE_ENTER = const StatementCompletionKind( 32 static const SIMPLE_ENTER = const StatementCompletionKind(
33 '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");
34 static const SIMPLE_SEMICOLON = const StatementCompletionKind( 34 static const SIMPLE_SEMICOLON = const StatementCompletionKind(
35 'SIMPLE_SEMICOLON', "Add a semicolon and newline"); 35 'SIMPLE_SEMICOLON', "Add a semicolon and newline");
36 static const COMPLETE_CLASS_DECLARATION = const StatementCompletionKind(
37 'COMPLETE_CLASS_DECLARATION', "Complete class declaration");
36 static const COMPLETE_CONTROL_FLOW_BLOCK = const StatementCompletionKind( 38 static const COMPLETE_CONTROL_FLOW_BLOCK = const StatementCompletionKind(
37 'COMPLETE_CONTROL_FLOW_BLOCK', "Complete control flow block"); 39 'COMPLETE_CONTROL_FLOW_BLOCK', "Complete control flow block");
38 static const COMPLETE_DO_STMT = const StatementCompletionKind( 40 static const COMPLETE_DO_STMT = const StatementCompletionKind(
39 'COMPLETE_DO_STMT', "Complete do-statement"); 41 'COMPLETE_DO_STMT', "Complete do-statement");
40 static const COMPLETE_IF_STMT = const StatementCompletionKind( 42 static const COMPLETE_IF_STMT = const StatementCompletionKind(
41 'COMPLETE_IF_STMT', "Complete if-statement"); 43 'COMPLETE_IF_STMT', "Complete if-statement");
42 static const COMPLETE_FOR_STMT = const StatementCompletionKind( 44 static const COMPLETE_FOR_STMT = const StatementCompletionKind(
43 'COMPLETE_FOR_STMT', "Complete for-statement"); 45 'COMPLETE_FOR_STMT', "Complete for-statement");
44 static const COMPLETE_FOR_EACH_STMT = const StatementCompletionKind( 46 static const COMPLETE_FOR_EACH_STMT = const StatementCompletionKind(
45 'COMPLETE_FOR_EACH_STMT', "Complete for-each-statement"); 47 'COMPLETE_FOR_EACH_STMT', "Complete for-each-statement");
48 static const COMPLETE_FUNCTION_DECLARATION = const StatementCompletionKind(
49 'COMPLETE_FUNCTION_DECLARATION', "Complete function declaration");
46 static const COMPLETE_SWITCH_STMT = const StatementCompletionKind( 50 static const COMPLETE_SWITCH_STMT = const StatementCompletionKind(
47 'COMPLETE_SWITCH_STMT', "Complete switch-statement"); 51 'COMPLETE_SWITCH_STMT', "Complete switch-statement");
48 static const COMPLETE_TRY_STMT = const StatementCompletionKind( 52 static const COMPLETE_TRY_STMT = const StatementCompletionKind(
49 'COMPLETE_TRY_STMT', "Complete try-statement"); 53 'COMPLETE_TRY_STMT', "Complete try-statement");
54 static const COMPLETE_VARIABLE_DECLARATION = const StatementCompletionKind(
55 'COMPLETE_VARIABLE_DECLARATION', "Complete variable declaration");
50 static const COMPLETE_WHILE_STMT = const StatementCompletionKind( 56 static const COMPLETE_WHILE_STMT = const StatementCompletionKind(
51 'COMPLETE_WHILE_STMT', "Complete while-statement"); 57 'COMPLETE_WHILE_STMT', "Complete while-statement");
52 } 58 }
53 59
54 /** 60 /**
55 * A description of a statement completion. 61 * A description of a statement completion.
56 * 62 *
57 * Clients may not extend, implement or mix-in this class. 63 * Clients may not extend, implement or mix-in this class.
58 */ 64 */
59 class StatementCompletion { 65 class StatementCompletion {
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 CompilationUnit get unit => statementContext.unit; 167 CompilationUnit get unit => statementContext.unit;
162 168
163 CompilationUnitElement get unitElement => statementContext.unitElement; 169 CompilationUnitElement get unitElement => statementContext.unitElement;
164 170
165 Future<StatementCompletion> compute() async { 171 Future<StatementCompletion> compute() async {
166 // If the source was changed between the constructor and running 172 // If the source was changed between the constructor and running
167 // this asynchronous method, it is not safe to use the unit. 173 // this asynchronous method, it is not safe to use the unit.
168 if (analysisContext.getModificationStamp(source) != fileStamp) { 174 if (analysisContext.getModificationStamp(source) != fileStamp) {
169 return NO_COMPLETION; 175 return NO_COMPLETION;
170 } 176 }
171 node = new NodeLocator(selectionOffset).searchWithin(unit); 177 node = _selectedNode();
172 if (node == null) { 178 if (node == null) {
173 return NO_COMPLETION; 179 return NO_COMPLETION;
174 } 180 }
175 // TODO(messick): This needs to work for declarations. 181 node = node
176 AstNode newNode = node.getAncestor((n) => n is Statement); 182 .getAncestor((n) => n is Statement || _isNonStatementDeclaration(n));
177 if (newNode is Block) { 183 if (node == null) {
178 Block blockNode = newNode; 184 return _complete_simpleEnter() ? completion : NO_COMPLETION;
185 }
186 if (node is Block) {
187 Block blockNode = node;
179 if (blockNode.statements.isNotEmpty) { 188 if (blockNode.statements.isNotEmpty) {
180 node = blockNode.statements.last; 189 node = blockNode.statements.last;
181 } else {
182 node = newNode;
183 } 190 }
184 } else {
185 node = newNode;
186 } 191 }
187 if (_isEmptyStatement(node)) { 192 if (_isEmptyStatement(node)) {
188 node = node.parent; 193 node = node.parent;
189 } 194 }
190 for (engine.AnalysisError error in statementContext.errors) { 195 for (engine.AnalysisError error in statementContext.errors) {
191 if (error.offset >= node.offset && 196 if (error.offset >= node.offset &&
192 error.offset <= node.offset + node.length) { 197 error.offset <= node.offset + node.length) {
193 if (error.errorCode is! HintCode) { 198 if (error.errorCode is! HintCode) {
194 errors.add(error); 199 errors.add(error);
195 } 200 }
196 } 201 }
197 } 202 }
198 203
199 if (errors.isEmpty) { 204 if (node is Statement) {
200 if (_complete_controlFlowBlock() || _complete_simpleEnter()) { 205 if (errors.isEmpty) {
201 return completion; 206 if (_complete_ifStatement() ||
207 _complete_forStatement() ||
208 _complete_forEachStatement() ||
209 _complete_whileStatement() ||
210 _complete_controlFlowBlock()) {
211 return completion;
212 }
213 } else {
214 if (_complete_ifStatement() ||
215 _complete_doStatement() ||
216 _complete_forStatement() ||
217 _complete_forEachStatement() ||
218 _complete_functionDeclarationStatement() ||
219 _complete_switchStatement() ||
220 _complete_tryStatement() ||
221 _complete_whileStatement() ||
222 _complete_controlFlowBlock() ||
223 _complete_simpleSemicolon() ||
224 _complete_methodCall()) {
225 return completion;
226 }
202 } 227 }
203 } else { 228 } else if (node is Declaration) {
204 if (_complete_ifStatement() || 229 if (errors.isNotEmpty) {
205 _complete_doStatement() || 230 if (_complete_classDeclaration() ||
206 _complete_forStatement() || 231 _complete_functionDeclaration() ||
207 _complete_forEachStatement() || 232 _complete_variableDeclaration()) {
208 _complete_switchStatement() || 233 return completion;
209 _complete_tryStatement() || 234 }
210 _complete_whileStatement() ||
211 _complete_controlFlowBlock() ||
212 _complete_simpleSemicolon() ||
213 _complete_simpleEnter()) {
214 return completion;
215 } 235 }
216 } 236 }
237 if (_complete_simpleEnter()) {
238 return completion;
239 }
217 return NO_COMPLETION; 240 return NO_COMPLETION;
218 } 241 }
219 242
220 void _addInsertEdit(int offset, String text) { 243 void _addInsertEdit(int offset, String text) {
221 SourceEdit edit = new SourceEdit(offset, 0, text); 244 SourceEdit edit = new SourceEdit(offset, 0, text);
222 doSourceChange_addElementEdit(change, unitElement, edit); 245 doSourceChange_addElementEdit(change, unitElement, edit);
223 } 246 }
224 247
225 void _addReplaceEdit(SourceRange range, String text) { 248 void _addReplaceEdit(SourceRange range, String text) {
226 SourceEdit edit = new SourceEdit(range.offset, range.length, text); 249 SourceEdit edit = new SourceEdit(range.offset, range.length, text);
(...skipping 28 matching lines...) Expand all
255 } 278 }
256 279
257 String _baseNodeText(AstNode astNode) { 280 String _baseNodeText(AstNode astNode) {
258 String text = utils.getNodeText(astNode); 281 String text = utils.getNodeText(astNode);
259 if (text.endsWith(eol)) { 282 if (text.endsWith(eol)) {
260 text = text.substring(0, text.length - eol.length); 283 text = text.substring(0, text.length - eol.length);
261 } 284 }
262 return text; 285 return text;
263 } 286 }
264 287
288 bool _complete_classDeclaration() {
289 if (node is! ClassDeclaration) {
290 return false;
291 }
292 ClassDeclaration decl = node;
293 if (decl.leftBracket.isSynthetic && errors.length == 1) {
294 // 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);
296 sb.append(' ');
297 _appendEmptyBraces(sb, true);
298 _insertBuilder(sb);
299 _setCompletion(DartStatementCompletion.COMPLETE_CLASS_DECLARATION);
300 return true;
301 }
302 return false;
303 }
304
265 bool _complete_controlFlowBlock() { 305 bool _complete_controlFlowBlock() {
266 Expression expr = (node is ExpressionStatement) 306 Expression expr = (node is ExpressionStatement)
267 ? (node as ExpressionStatement).expression 307 ? (node as ExpressionStatement).expression
268 : (node is ReturnStatement 308 : (node is ReturnStatement
269 ? (node as ReturnStatement).expression 309 ? (node as ReturnStatement).expression
270 : null); 310 : null);
271 if (!(node is ReturnStatement || expr is ThrowExpression)) { 311 if (!(node is ReturnStatement || expr is ThrowExpression)) {
272 return false; 312 return false;
273 } 313 }
274 if (node.parent is! Block) { 314 if (node.parent is! Block) {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 } 355 }
316 356
317 bool _complete_doStatement() { 357 bool _complete_doStatement() {
318 if (node is! DoStatement) { 358 if (node is! DoStatement) {
319 return false; 359 return false;
320 } 360 }
321 DoStatement statement = node; 361 DoStatement statement = node;
322 SourceBuilder sb = _sourceBuilderAfterKeyword(statement.doKeyword); 362 SourceBuilder sb = _sourceBuilderAfterKeyword(statement.doKeyword);
323 bool hasWhileKeyword = statement.whileKeyword.lexeme == "while"; 363 bool hasWhileKeyword = statement.whileKeyword.lexeme == "while";
324 int exitDelta = 0; 364 int exitDelta = 0;
325 if (statement.body is EmptyStatement) { 365 if (!_statementHasValidBody(statement.doKeyword, statement.body)) {
326 String text = utils.getNodeText(statement.body); 366 String text = utils.getNodeText(statement.body);
327 int delta = 0; 367 int delta = 0;
328 if (text.startsWith(';')) { 368 if (text.startsWith(';')) {
329 delta = 1; 369 delta = 1;
330 _addReplaceEdit(rangeStartLength(statement.body.offset, delta), ''); 370 _addReplaceEdit(rangeStartLength(statement.body.offset, delta), '');
331 if (hasWhileKeyword) { 371 if (hasWhileKeyword) {
332 text = utils.getNodeText(statement); 372 text = utils.getNodeText(statement);
333 if (text.indexOf(new RegExp(r'do\s*;\s*while')) == 0) { 373 if (text.indexOf(new RegExp(r'do\s*;\s*while')) == 0) {
334 int end = text.indexOf('while'); 374 int end = text.indexOf('while');
335 int start = text.indexOf(';') + 1; 375 int start = text.indexOf(';') + 1;
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
423 } else if (_isSyntheticExpression(forNode.iterable)) { 463 } else if (_isSyntheticExpression(forNode.iterable)) {
424 exitPosition = new Position(file, forNode.rightParenthesis.offset + 1); 464 exitPosition = new Position(file, forNode.rightParenthesis.offset + 1);
425 src = src.substring(forNode.inKeyword.offset - forNode.offset); 465 src = src.substring(forNode.inKeyword.offset - forNode.offset);
426 if (src.startsWith(new RegExp(r'in\s*\)'))) { 466 if (src.startsWith(new RegExp(r'in\s*\)'))) {
427 _addReplaceEdit( 467 _addReplaceEdit(
428 rangeStartEnd(forNode.inKeyword.offset + forNode.inKeyword.length, 468 rangeStartEnd(forNode.inKeyword.offset + forNode.inKeyword.length,
429 forNode.rightParenthesis.offset), 469 forNode.rightParenthesis.offset),
430 ' '); 470 ' ');
431 } 471 }
432 } 472 }
433 if (_isEmptyStatement(forNode.body)) { 473 if (!_statementHasValidBody(forNode.forKeyword, forNode.body)) {
434 sb.append(' '); 474 sb.append(' ');
435 _appendEmptyBraces(sb, exitPosition == null); 475 _appendEmptyBraces(sb, exitPosition == null);
436 } 476 }
437 _insertBuilder(sb); 477 _insertBuilder(sb);
438 _setCompletion(DartStatementCompletion.COMPLETE_FOR_EACH_STMT); 478 _setCompletion(DartStatementCompletion.COMPLETE_FOR_EACH_STMT);
439 return true; 479 return true;
440 } 480 }
441 481
442 bool _complete_forStatement() { 482 bool _complete_forStatement() {
443 if (node is! ForStatement) { 483 if (node is! ForStatement) {
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 _addReplaceEdit(rangeStartLength(start, end), '; '); 539 _addReplaceEdit(rangeStartLength(start, end), '; ');
500 delta = end - '; '.length; 540 delta = end - '; '.length;
501 exitPosition = new Position(file, start); 541 exitPosition = new Position(file, start);
502 } else { 542 } else {
503 // Not possible; any comment following init is attached to init. 543 // Not possible; any comment following init is attached to init.
504 exitPosition = _newPosition(forNode.rightParenthesis.offset); 544 exitPosition = _newPosition(forNode.rightParenthesis.offset);
505 sb = new SourceBuilder(file, forNode.rightParenthesis.offset); 545 sb = new SourceBuilder(file, forNode.rightParenthesis.offset);
506 } 546 }
507 } 547 }
508 } 548 }
509 if (forNode.body is EmptyStatement) { 549 if (!_statementHasValidBody(forNode.forKeyword, forNode.body)) {
510 // keywordOnly 550 // keywordOnly, noError
511 sb.append(' '); 551 sb.append(' ');
512 _appendEmptyBraces(sb, exitPosition == null); 552 _appendEmptyBraces(sb, exitPosition == null);
513 } 553 }
514 if (delta != 0 && exitPosition != null) { 554 if (delta != 0 && exitPosition != null) {
515 // missingLeftSeparator 555 // missingLeftSeparator
516 exitPosition = new Position(file, exitPosition.offset - delta); 556 exitPosition = new Position(file, exitPosition.offset - delta);
517 } 557 }
518 _insertBuilder(sb); 558 _insertBuilder(sb);
519 _setCompletion(DartStatementCompletion.COMPLETE_FOR_STMT); 559 _setCompletion(DartStatementCompletion.COMPLETE_FOR_STMT);
520 return true; 560 return true;
521 } 561 }
522 562
563 bool _complete_functionDeclaration() {
564 if (node is! MethodDeclaration && node is! FunctionDeclaration) {
565 return false;
566 }
567 SourceBuilder sb = new SourceBuilder(file, node.end - 1);
568 sb.append(' ');
569 _appendEmptyBraces(sb, true);
570 _insertBuilder(sb);
571 _setCompletion(DartStatementCompletion.COMPLETE_FUNCTION_DECLARATION);
572 return true;
573 }
574
575 bool _complete_functionDeclarationStatement() {
576 if (node is! FunctionDeclarationStatement) {
577 return false;
578 }
579 var error = _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
580 if (error != null) {
581 FunctionDeclarationStatement stmt = node;
582 String src = utils.getNodeText(stmt);
583 int insertOffset = stmt.functionDeclaration.end - 1;
584 if (stmt.functionDeclaration.functionExpression.body
585 is ExpressionFunctionBody) {
586 ExpressionFunctionBody fnb =
587 stmt.functionDeclaration.functionExpression.body;
588 int fnbOffset = fnb.functionDefinition.offset;
589 String fnSrc = src.substring(fnbOffset - stmt.offset);
590 if (!fnSrc.startsWith('=>')) {
591 return false;
592 }
593 int delta = 0;
594 if (fnb.expression.isSynthetic) {
595 if (!fnSrc.startsWith('=> ')) {
596 _addInsertEdit(insertOffset, ' ');
597 delta = 1;
598 }
599 _addInsertEdit(insertOffset, ';');
600 _appendNewlinePlusIndentAt(insertOffset);
601 } else {
602 delta = 1;
603 _addInsertEdit(insertOffset, ';');
604 insertOffset = _appendNewlinePlusIndent();
605 }
606 _setCompletionAt(
607 DartStatementCompletion.SIMPLE_SEMICOLON, insertOffset + delta);
608 return true;
609 }
610 }
611 return false;
612 }
613
523 bool _complete_ifOrWhileStatement( 614 bool _complete_ifOrWhileStatement(
524 _KeywordConditionBlockStructure statement, StatementCompletionKind kind) { 615 _KeywordConditionBlockStructure statement, StatementCompletionKind kind) {
616 if (_statementHasValidBody(statement.keyword, statement.block)) {
617 return false;
618 }
525 SourceBuilder sb = _complete_keywordCondition(statement); 619 SourceBuilder sb = _complete_keywordCondition(statement);
526 if (sb == null) { 620 if (sb == null) {
527 return false; 621 return false;
528 } 622 }
529 if (statement.block is EmptyStatement) { 623 sb.append(' ');
530 sb.append(' '); 624 _appendEmptyBraces(sb, exitPosition == null);
531 _appendEmptyBraces(sb, exitPosition == null);
532 }
533 _insertBuilder(sb); 625 _insertBuilder(sb);
534 _setCompletion(kind); 626 _setCompletion(kind);
535 return true; 627 return true;
536 } 628 }
537 629
538 bool _complete_ifStatement() { 630 bool _complete_ifStatement() {
539 if (node is! IfStatement) { 631 if (node is! IfStatement) {
540 return false; 632 return false;
541 } 633 }
542 IfStatement ifNode = node; 634 IfStatement ifNode = node;
543 if (ifNode != null) { 635 if (ifNode.elseKeyword != null) {
544 if (ifNode.elseKeyword != null) { 636 return false;
545 return false;
546 }
547 var stmt = new _KeywordConditionBlockStructure(
548 ifNode.ifKeyword,
549 ifNode.leftParenthesis,
550 ifNode.condition,
551 ifNode.rightParenthesis,
552 ifNode.thenStatement);
553 return _complete_ifOrWhileStatement(
554 stmt, DartStatementCompletion.COMPLETE_IF_STMT);
555 } 637 }
556 return false; 638 var stmt = new _KeywordConditionBlockStructure(
639 ifNode.ifKeyword,
640 ifNode.leftParenthesis,
641 ifNode.condition,
642 ifNode.rightParenthesis,
643 ifNode.thenStatement);
644 return _complete_ifOrWhileStatement(
645 stmt, DartStatementCompletion.COMPLETE_IF_STMT);
557 } 646 }
558 647
559 SourceBuilder _complete_keywordCondition( 648 SourceBuilder _complete_keywordCondition(
560 _KeywordConditionBlockStructure statement) { 649 _KeywordConditionBlockStructure statement) {
561 SourceBuilder sb; 650 SourceBuilder sb;
562 if (statement.leftParenthesis.isSynthetic) { 651 if (statement.leftParenthesis.isSynthetic) {
563 if (!statement.rightParenthesis.isSynthetic) { 652 if (!statement.rightParenthesis.isSynthetic) {
564 // Quite unlikely to see this so don't try to fix it. 653 // Quite unlikely to see this so don't try to fix it.
565 return null; 654 return null;
566 } 655 }
567 sb = _sourceBuilderAfterKeyword(statement.keyword); 656 sb = _sourceBuilderAfterKeyword(statement.keyword);
568 sb.append('('); 657 sb.append('(');
569 sb.setExitOffset(); 658 sb.setExitOffset();
570 sb.append(')'); 659 sb.append(')');
571 } else { 660 } else {
572 if (_isSyntheticExpression(statement.condition)) { 661 if (_isSyntheticExpression(statement.condition)) {
573 exitPosition = _newPosition(statement.leftParenthesis.offset + 1); 662 exitPosition = _newPosition(statement.leftParenthesis.offset + 1);
574 sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1); 663 sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1);
575 } else { 664 } else {
576 sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1); 665 sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1);
577 } 666 }
578 } 667 }
579 return sb; 668 return sb;
580 } 669 }
581 670
671 bool _complete_methodCall() {
672 var parenError =
673 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "')'");
674 if (parenError == null) {
675 return false;
676 }
677 AstNode argList = _selectedNode(at: parenError.offset - 1)
678 .getAncestor((n) => n is ArgumentList);
679 if (argList?.getAncestor((n) => n == node) == null) {
680 return false;
681 }
682 int loc = argList.end - 1;
683 int delta = 1;
684 var semicolonError =
685 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
686 if (semicolonError == null) {
687 loc += 1;
688 delta = 0;
689 }
690 _addInsertEdit(loc, ')');
691 if (semicolonError != null) {
692 _addInsertEdit(loc, ';');
693 }
694 String indent = utils.getLinePrefix(selectionOffset);
695 int exit = utils.getLineNext(selectionOffset);
696 _addInsertEdit(exit, indent + eol);
697 exit += indent.length + eol.length;
698
699 _setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, exit + delta);
700 return true;
701 }
702
582 bool _complete_simpleEnter() { 703 bool _complete_simpleEnter() {
583 int offset; 704 int offset;
584 if (!errors.isEmpty) { 705 if (!errors.isEmpty) {
585 offset = selectionOffset; 706 offset = selectionOffset;
586 } else { 707 } else {
587 String indent = utils.getLinePrefix(selectionOffset); 708 String indent = utils.getLinePrefix(selectionOffset);
588 int loc = utils.getLineNext(selectionOffset); 709 int loc = utils.getLineNext(selectionOffset);
589 _addInsertEdit(loc, indent + eol); 710 _addInsertEdit(loc, indent + eol);
590 offset = loc + indent.length + eol.length; 711 offset = loc + indent.length + eol.length;
591 } 712 }
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
741 sb = new SourceBuilder(file, tryNode.finallyKeyword.end); 862 sb = new SourceBuilder(file, tryNode.finallyKeyword.end);
742 sb.append(' '); 863 sb.append(' ');
743 _appendEmptyBraces(sb, true); 864 _appendEmptyBraces(sb, true);
744 _insertBuilder(sb); 865 _insertBuilder(sb);
745 } 866 }
746 } 867 }
747 _setCompletion(DartStatementCompletion.COMPLETE_TRY_STMT); 868 _setCompletion(DartStatementCompletion.COMPLETE_TRY_STMT);
748 return true; 869 return true;
749 } 870 }
750 871
872 bool _complete_variableDeclaration() {
873 if (node is! VariableDeclaration) {
874 return false;
875 }
876 _addInsertEdit(node.end, ';');
877 exitPosition = new Position(file, _appendNewlinePlusIndentAt(node.end) + 1);
878 _setCompletion(DartStatementCompletion.COMPLETE_VARIABLE_DECLARATION);
879 return true;
880 }
881
751 bool _complete_whileStatement() { 882 bool _complete_whileStatement() {
752 if (node is! WhileStatement) { 883 if (node is! WhileStatement) {
753 return false; 884 return false;
754 } 885 }
755 WhileStatement whileNode = node; 886 WhileStatement whileNode = node;
756 if (whileNode != null) { 887 if (whileNode != null) {
757 var stmt = new _KeywordConditionBlockStructure( 888 var stmt = new _KeywordConditionBlockStructure(
758 whileNode.whileKeyword, 889 whileNode.whileKeyword,
759 whileNode.leftParenthesis, 890 whileNode.leftParenthesis,
760 whileNode.condition, 891 whileNode.condition,
761 whileNode.rightParenthesis, 892 whileNode.rightParenthesis,
762 whileNode.body); 893 whileNode.body);
763 return _complete_ifOrWhileStatement( 894 return _complete_ifOrWhileStatement(
764 stmt, DartStatementCompletion.COMPLETE_WHILE_STMT); 895 stmt, DartStatementCompletion.COMPLETE_WHILE_STMT);
765 } 896 }
766 return false; 897 return false;
767 } 898 }
768 899
769 engine.AnalysisError _findError(ErrorCode code, {partialMatch: null}) { 900 engine.AnalysisError _findError(ErrorCode code, {partialMatch: null}) {
770 var error = 901 return errors.firstWhere(
771 errors.firstWhere((err) => err.errorCode == code, orElse: () => null); 902 (err) =>
772 if (error != null) { 903 err.errorCode == code &&
773 if (partialMatch != null) { 904 (partialMatch == null ? true : err.message.contains(partialMatch)),
774 return error.message.contains(partialMatch) ? error : null; 905 orElse: () => null);
775 }
776 return error;
777 }
778 return null;
779 } 906 }
780 907
781 T _findInvalidElement<T extends AstNode>(NodeList<T> list) { 908 T _findInvalidElement<T extends AstNode>(NodeList<T> list) {
782 return list.firstWhere( 909 return list.firstWhere(
783 (catchClause) => 910 (item) => selectionOffset >= item.offset && selectionOffset <= item.end,
784 selectionOffset >= catchClause.offset &&
785 selectionOffset <= catchClause.end,
786 orElse: () => null); 911 orElse: () => null);
787 } 912 }
788 913
789 LinkedEditGroup _getLinkedPosition(String groupId) { 914 LinkedEditGroup _getLinkedPosition(String groupId) {
790 LinkedEditGroup group = linkedPositionGroups[groupId]; 915 LinkedEditGroup group = linkedPositionGroups[groupId];
791 if (group == null) { 916 if (group == null) {
792 group = new LinkedEditGroup.empty(); 917 group = new LinkedEditGroup.empty();
793 linkedPositionGroups[groupId] = group; 918 linkedPositionGroups[groupId] = group;
794 } 919 }
795 return group; 920 return group;
(...skipping 25 matching lines...) Expand all
821 } 946 }
822 947
823 bool _isEmptyBlock(AstNode stmt) { 948 bool _isEmptyBlock(AstNode stmt) {
824 return stmt is Block && stmt.statements.isEmpty; 949 return stmt is Block && stmt.statements.isEmpty;
825 } 950 }
826 951
827 bool _isEmptyStatement(AstNode stmt) { 952 bool _isEmptyStatement(AstNode stmt) {
828 return stmt is EmptyStatement || _isEmptyBlock(stmt); 953 return stmt is EmptyStatement || _isEmptyBlock(stmt);
829 } 954 }
830 955
956 bool _isNonStatementDeclaration(AstNode n) {
957 if (n is! Declaration) {
958 return false;
959 }
960 if (n is! VariableDeclaration && n is! FunctionDeclaration) {
961 return true;
962 }
963 AstNode p = n.parent;
964 return p is! Statement && p?.parent is! Statement;
965 }
966
831 bool _isSyntheticExpression(Expression expr) { 967 bool _isSyntheticExpression(Expression expr) {
832 return expr is SimpleIdentifier && expr.isSynthetic; 968 return expr is SimpleIdentifier && expr.isSynthetic;
833 } 969 }
834 970
835 Position _newPosition(int offset) { 971 Position _newPosition(int offset) {
836 return new Position(file, offset); 972 return new Position(file, offset);
837 } 973 }
838 974
975 AstNode _selectedNode({int at: null}) =>
976 new NodeLocator(at == null ? selectionOffset : at).searchWithin(unit);
977
839 void _setCompletion(StatementCompletionKind kind, [List args]) { 978 void _setCompletion(StatementCompletionKind kind, [List args]) {
840 assert(exitPosition != null); 979 assert(exitPosition != null);
841 change.selection = exitPosition; 980 change.selection = exitPosition;
842 change.message = formatList(kind.message, args); 981 change.message = formatList(kind.message, args);
843 linkedPositionGroups.values 982 linkedPositionGroups.values
844 .forEach((group) => change.addLinkedEditGroup(group)); 983 .forEach((group) => change.addLinkedEditGroup(group));
845 completion = new StatementCompletion(kind, change); 984 completion = new StatementCompletion(kind, change);
846 } 985 }
847 986
848 void _setCompletionAt(StatementCompletionKind kind, int offset, [List args]) { 987 void _setCompletionAt(StatementCompletionKind kind, int offset, [List args]) {
849 exitPosition = _newPosition(offset); 988 exitPosition = _newPosition(offset);
850 _setCompletion(kind, args); 989 _setCompletion(kind, args);
851 } 990 }
852 991
853 SourceBuilder _sourceBuilderAfterKeyword(Token keyword) { 992 SourceBuilder _sourceBuilderAfterKeyword(Token keyword) {
854 SourceBuilder sb; 993 SourceBuilder sb;
855 String text = _baseNodeText(node); 994 String text = _baseNodeText(node);
856 text = text.substring(keyword.offset - node.offset); 995 text = text.substring(keyword.offset - node.offset);
857 int len = keyword.length; 996 int len = keyword.length;
858 if (text.length == len || 997 if (text.length == len ||
859 !text.substring(len, len + 1).contains(new RegExp(r'\s'))) { 998 !text.substring(len, len + 1).contains(new RegExp(r'\s'))) {
860 sb = new SourceBuilder(file, keyword.offset + len); 999 sb = new SourceBuilder(file, keyword.offset + len);
861 sb.append(' '); 1000 sb.append(' ');
862 } else { 1001 } else {
863 sb = new SourceBuilder(file, keyword.offset + len + 1); 1002 sb = new SourceBuilder(file, keyword.offset + len + 1);
864 } 1003 }
865 return sb; 1004 return sb;
866 } 1005 }
1006
1007 bool _statementHasValidBody(Token keyword, Statement body) {
1008 // A "valid" body is either a non-synthetic block or a single statement
1009 // on the same line as the parent statement, similar to dart_style.
1010 if (body.isSynthetic) {
1011 return false;
1012 }
1013 if (body is Block) {
1014 Block block = body;
1015 return (!(block.leftBracket.isSynthetic));
1016 }
1017 return (lineInfo.getLocation(keyword.offset) ==
1018 lineInfo.getLocation(body.offset));
1019 }
867 } 1020 }
868 1021
869 // Encapsulate common structure of if-statement and while-statement. 1022 // Encapsulate common structure of if-statement and while-statement.
870 class _KeywordConditionBlockStructure { 1023 class _KeywordConditionBlockStructure {
871 final Token keyword; 1024 final Token keyword;
872 final Token leftParenthesis, rightParenthesis; 1025 final Token leftParenthesis, rightParenthesis;
873 final Expression condition; 1026 final Expression condition;
874 final Statement block; 1027 final Statement block;
875 1028
876 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis, 1029 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis,
877 this.condition, this.rightParenthesis, this.block); 1030 this.condition, this.rightParenthesis, this.block);
878 1031
879 int get offset => keyword.offset; 1032 int get offset => keyword.offset;
880 } 1033 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698