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

Side by Side Diff: pkg/analysis_services/lib/src/correction/assist.dart

Issue 484733003: Import analysis_services.dart into analysis_server.dart. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
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.
4
5 library services.src.correction.assist;
6
7 import 'dart:collection';
8
9 import 'package:analysis_services/correction/assist.dart';
10 import 'package:analysis_services/correction/change.dart';
11 import 'package:analysis_services/search/hierarchy.dart';
12 import 'package:analysis_services/search/search_engine.dart';
13 import 'package:analysis_services/src/correction/name_suggestion.dart';
14 import 'package:analysis_services/src/correction/source_buffer.dart';
15 import 'package:analysis_services/src/correction/source_range.dart';
16 import 'package:analysis_services/src/correction/statement_analyzer.dart';
17 import 'package:analysis_services/src/correction/util.dart';
18 import 'package:analyzer/src/generated/ast.dart';
19 import 'package:analyzer/src/generated/element.dart';
20 import 'package:analyzer/src/generated/java_core.dart';
21 import 'package:analyzer/src/generated/scanner.dart';
22 import 'package:analyzer/src/generated/source.dart';
23 import 'package:path/path.dart';
24
25
26
27 typedef _SimpleIdentifierVisitor(SimpleIdentifier node);
28
29
30 /**
31 * The computer for Dart assists.
32 */
33 class AssistProcessor {
34 final SearchEngine searchEngine;
35 final Source source;
36 final String file;
37 final CompilationUnit unit;
38 final int selectionOffset;
39 final int selectionLength;
40 CompilationUnitElement unitElement;
41 LibraryElement unitLibraryElement;
42 String unitLibraryFile;
43 String unitLibraryFolder;
44
45 final List<Edit> edits = <Edit>[];
46 final Map<String, LinkedEditGroup> linkedPositionGroups = <String,
47 LinkedEditGroup>{};
48 Position exitPosition = null;
49 final List<Assist> assists = <Assist>[];
50
51 int selectionEnd;
52 CorrectionUtils utils;
53 AstNode node;
54
55 AssistProcessor(this.searchEngine, this.source, this.file, this.unit,
56 this.selectionOffset, this.selectionLength) {
57 unitElement = unit.element;
58 unitLibraryElement = unitElement.library;
59 unitLibraryFile = unitLibraryElement.source.fullName;
60 unitLibraryFolder = dirname(unitLibraryFile);
61 selectionEnd = selectionOffset + selectionLength;
62 }
63
64 /**
65 * Returns the EOL to use for this [CompilationUnit].
66 */
67 String get eol => utils.endOfLine;
68
69 List<Assist> compute() {
70 utils = new CorrectionUtils(unit);
71 node = new NodeLocator.con2(
72 selectionOffset,
73 selectionEnd).searchWithin(unit);
74 // try to add proposals
75 _addProposal_addTypeAnnotation();
76 _addProposal_assignToLocalVariable();
77 _addProposal_convertToBlockFunctionBody();
78 _addProposal_convertToExpressionFunctionBody();
79 _addProposal_convertToIsNot_onIs();
80 _addProposal_convertToIsNot_onNot();
81 _addProposal_convertToIsNotEmpty();
82 _addProposal_exchangeOperands();
83 _addProposal_importAddShow();
84 _addProposal_invertIf();
85 _addProposal_joinIfStatementInner();
86 _addProposal_joinIfStatementOuter();
87 _addProposal_joinVariableDeclaration_onAssignment();
88 _addProposal_joinVariableDeclaration_onDeclaration();
89 _addProposal_removeTypeAnnotation();
90 _addProposal_replaceConditionalWithIfElse();
91 _addProposal_replaceIfElseWithConditional();
92 _addProposal_splitAndCondition();
93 _addProposal_splitVariableDeclaration();
94 _addProposal_surroundWith();
95 // done
96 return assists;
97 }
98
99 FunctionBody getEnclosingFunctionBody() {
100 {
101 FunctionExpression function =
102 node.getAncestor((node) => node is FunctionExpression);
103 if (function != null) {
104 return function.body;
105 }
106 }
107 {
108 FunctionDeclaration function =
109 node.getAncestor((node) => node is FunctionDeclaration);
110 if (function != null) {
111 return function.functionExpression.body;
112 }
113 }
114 {
115 MethodDeclaration method =
116 node.getAncestor((node) => node is MethodDeclaration);
117 if (method != null) {
118 return method.body;
119 }
120 }
121 return null;
122 }
123
124 void _addAssist(AssistKind kind, List args, {String assistFile}) {
125 if (assistFile == null) {
126 assistFile = file;
127 }
128 FileEdit fileEdit = new FileEdit(file);
129 fileEdit.addAll(edits);
130 // prepare Change
131 String message = formatList(kind.message, args);
132 Change change = new Change(message);
133 change.addFileEdit(fileEdit);
134 linkedPositionGroups.values.forEach(
135 (group) => change.addLinkedEditGroup(group));
136 change.selection = exitPosition;
137 // add Assist
138 Assist assist = new Assist(kind, change);
139 assists.add(assist);
140 // clear
141 edits.clear();
142 linkedPositionGroups.clear();
143 exitPosition = null;
144 }
145
146 /**
147 * Adds a new [Edit] to [edits].
148 */
149 void _addInsertEdit(int offset, String text) {
150 Edit edit = new Edit(offset, 0, text);
151 edits.add(edit);
152 }
153
154 void _addProposal_addTypeAnnotation() {
155 // prepare VariableDeclarationList
156 VariableDeclarationList declarationList =
157 node.getAncestor((node) => node is VariableDeclarationList);
158 if (declarationList == null) {
159 _coverageMarker();
160 return;
161 }
162 // may be has type annotation already
163 if (declarationList.type != null) {
164 _coverageMarker();
165 return;
166 }
167 // prepare single VariableDeclaration
168 List<VariableDeclaration> variables = declarationList.variables;
169 if (variables.length != 1) {
170 _coverageMarker();
171 return;
172 }
173 VariableDeclaration variable = variables[0];
174 // we need an initializer to get the type from
175 Expression initializer = variable.initializer;
176 if (initializer == null) {
177 _coverageMarker();
178 return;
179 }
180 DartType type = initializer.bestType;
181 // prepare type source
182 String typeSource;
183 if (type is InterfaceType || type is FunctionType) {
184 typeSource = utils.getTypeSource(type);
185 } else {
186 _coverageMarker();
187 return;
188 }
189 // add edit
190 Token keyword = declarationList.keyword;
191 if (keyword is KeywordToken && keyword.keyword == Keyword.VAR) {
192 SourceRange range = rangeToken(keyword);
193 _addReplaceEdit(range, typeSource);
194 } else {
195 _addInsertEdit(variable.offset, '$typeSource ');
196 }
197 // add proposal
198 _addAssist(AssistKind.ADD_TYPE_ANNOTATION, []);
199 }
200
201 void _addProposal_assignToLocalVariable() {
202 // prepare enclosing ExpressionStatement
203 Statement statement = node.getAncestor((node) => node is Statement);
204 if (statement is! ExpressionStatement) {
205 _coverageMarker();
206 return;
207 }
208 ExpressionStatement expressionStatement = statement as ExpressionStatement;
209 // prepare expression
210 Expression expression = expressionStatement.expression;
211 int offset = expression.offset;
212 // ignore if already assignment
213 if (expression is AssignmentExpression) {
214 _coverageMarker();
215 return;
216 }
217 // ignore "throw"
218 if (expression is ThrowExpression) {
219 _coverageMarker();
220 return;
221 }
222 // prepare expression type
223 DartType type = expression.bestType;
224 if (type.isVoid) {
225 _coverageMarker();
226 return;
227 }
228 // prepare source
229 SourceBuilder builder = new SourceBuilder(file, offset);
230 builder.append("var ");
231 // prepare excluded names
232 Set<String> excluded = new Set<String>();
233 {
234 ScopedNameFinder scopedNameFinder = new ScopedNameFinder(offset);
235 expression.accept(scopedNameFinder);
236 excluded.addAll(scopedNameFinder.locals.keys.toSet());
237 }
238 // name(s)
239 {
240 List<String> suggestions =
241 getVariableNameSuggestionsForExpression(type, expression, excluded);
242 builder.startPosition("NAME");
243 for (int i = 0; i < suggestions.length; i++) {
244 String name = suggestions[i];
245 if (i == 0) {
246 builder.append(name);
247 }
248 builder.addSuggestion(LinkedEditSuggestionKind.VARIABLE, name);
249 }
250 builder.endPosition();
251 }
252 builder.append(" = ");
253 // add proposal
254 _insertBuilder(builder);
255 _addAssist(AssistKind.ASSIGN_TO_LOCAL_VARIABLE, []);
256 }
257
258 void _addProposal_convertToBlockFunctionBody() {
259 FunctionBody body = getEnclosingFunctionBody();
260 // prepare expression body
261 if (body is! ExpressionFunctionBody) {
262 _coverageMarker();
263 return;
264 }
265 Expression returnValue = (body as ExpressionFunctionBody).expression;
266 // prepare prefix
267 String prefix = utils.getNodePrefix(body.parent);
268 // add change
269 String indent = utils.getIndent(1);
270 String returnSource = 'return ' + _getNodeText(returnValue);
271 String newBodySource = "{$eol$prefix${indent}$returnSource;$eol$prefix}";
272 _addReplaceEdit(rangeNode(body), newBodySource);
273 // add proposal
274 _addAssist(AssistKind.CONVERT_INTO_BLOCK_BODY, []);
275 }
276
277 void _addProposal_convertToExpressionFunctionBody() {
278 // prepare current body
279 FunctionBody body = getEnclosingFunctionBody();
280 if (body is! BlockFunctionBody) {
281 _coverageMarker();
282 return;
283 }
284 // prepare return statement
285 List<Statement> statements = (body as BlockFunctionBody).block.statements;
286 if (statements.length != 1) {
287 _coverageMarker();
288 return;
289 }
290 if (statements[0] is! ReturnStatement) {
291 _coverageMarker();
292 return;
293 }
294 ReturnStatement returnStatement = statements[0] as ReturnStatement;
295 // prepare returned expression
296 Expression returnExpression = returnStatement.expression;
297 if (returnExpression == null) {
298 _coverageMarker();
299 return;
300 }
301 // add change
302 String newBodySource = "=> ${_getNodeText(returnExpression)}";
303 if (body.parent is! FunctionExpression ||
304 body.parent.parent is FunctionDeclaration) {
305 newBodySource += ";";
306 }
307 _addReplaceEdit(rangeNode(body), newBodySource);
308 // add proposal
309 _addAssist(AssistKind.CONVERT_INTO_EXPRESSION_BODY, []);
310 }
311
312 /**
313 * Converts "!isEmpty" -> "isNotEmpty" if possible.
314 */
315 void _addProposal_convertToIsNotEmpty() {
316 // prepare "expr.isEmpty"
317 AstNode isEmptyAccess = null;
318 SimpleIdentifier isEmptyIdentifier = null;
319 if (node is SimpleIdentifier) {
320 SimpleIdentifier identifier = node as SimpleIdentifier;
321 AstNode parent = identifier.parent;
322 // normal case (but rare)
323 if (parent is PropertyAccess) {
324 isEmptyIdentifier = parent.propertyName;
325 isEmptyAccess = parent;
326 }
327 // usual case
328 if (parent is PrefixedIdentifier) {
329 isEmptyIdentifier = parent.identifier;
330 isEmptyAccess = parent;
331 }
332 }
333 if (isEmptyIdentifier == null) {
334 _coverageMarker();
335 return;
336 }
337 // should be "isEmpty"
338 Element propertyElement = isEmptyIdentifier.bestElement;
339 if (propertyElement == null || "isEmpty" != propertyElement.name) {
340 _coverageMarker();
341 return;
342 }
343 // should have "isNotEmpty"
344 Element propertyTarget = propertyElement.enclosingElement;
345 if (propertyTarget == null ||
346 getChildren(propertyTarget, "isNotEmpty").isEmpty) {
347 _coverageMarker();
348 return;
349 }
350 // should be in PrefixExpression
351 if (isEmptyAccess.parent is! PrefixExpression) {
352 _coverageMarker();
353 return;
354 }
355 PrefixExpression prefixExpression =
356 isEmptyAccess.parent as PrefixExpression;
357 // should be !
358 if (prefixExpression.operator.type != TokenType.BANG) {
359 return;
360 }
361 // do replace
362 _addRemoveEdit(rangeStartStart(prefixExpression, prefixExpression.operand));
363 _addReplaceEdit(rangeNode(isEmptyIdentifier), "isNotEmpty");
364 // add proposal
365 _addAssist(AssistKind.CONVERT_INTO_IS_NOT_EMPTY, []);
366 }
367
368 void _addProposal_convertToIsNot_onIs() {
369 // may be child of "is"
370 AstNode node = this.node;
371 while (node != null && node is! IsExpression) {
372 node = node.parent;
373 }
374 // prepare "is"
375 if (node is! IsExpression) {
376 _coverageMarker();
377 return;
378 }
379 IsExpression isExpression = node as IsExpression;
380 if (isExpression.notOperator != null) {
381 _coverageMarker();
382 return;
383 }
384 // prepare enclosing ()
385 AstNode parent = isExpression.parent;
386 if (parent is! ParenthesizedExpression) {
387 _coverageMarker();
388 return;
389 }
390 ParenthesizedExpression parExpression = parent as ParenthesizedExpression;
391 // prepare enclosing !()
392 AstNode parent2 = parent.parent;
393 if (parent2 is! PrefixExpression) {
394 _coverageMarker();
395 return;
396 }
397 PrefixExpression prefExpression = parent2 as PrefixExpression;
398 if (prefExpression.operator.type != TokenType.BANG) {
399 _coverageMarker();
400 return;
401 }
402 // strip !()
403 if (getExpressionParentPrecedence(prefExpression) >=
404 TokenType.IS.precedence) {
405 _addRemoveEdit(rangeToken(prefExpression.operator));
406 } else {
407 _addRemoveEdit(
408 rangeStartEnd(prefExpression, parExpression.leftParenthesis));
409 _addRemoveEdit(
410 rangeStartEnd(parExpression.rightParenthesis, prefExpression));
411 }
412 _addInsertEdit(isExpression.isOperator.end, "!");
413 // add proposal
414 _addAssist(AssistKind.CONVERT_INTO_IS_NOT, []);
415 }
416
417 void _addProposal_convertToIsNot_onNot() {
418 // may be () in prefix expression
419 if (node is ParenthesizedExpression && node.parent is PrefixExpression) {
420 node = node.parent;
421 }
422 // prepare !()
423 if (node is! PrefixExpression) {
424 _coverageMarker();
425 return;
426 }
427 PrefixExpression prefExpression = node as PrefixExpression;
428 // should be ! operator
429 if (prefExpression.operator.type != TokenType.BANG) {
430 _coverageMarker();
431 return;
432 }
433 // prepare !()
434 Expression operand = prefExpression.operand;
435 if (operand is! ParenthesizedExpression) {
436 _coverageMarker();
437 return;
438 }
439 ParenthesizedExpression parExpression = operand as ParenthesizedExpression;
440 operand = parExpression.expression;
441 // prepare "is"
442 if (operand is! IsExpression) {
443 _coverageMarker();
444 return;
445 }
446 IsExpression isExpression = operand as IsExpression;
447 if (isExpression.notOperator != null) {
448 _coverageMarker();
449 return;
450 }
451 // strip !()
452 if (getExpressionParentPrecedence(prefExpression) >=
453 TokenType.IS.precedence) {
454 _addRemoveEdit(rangeToken(prefExpression.operator));
455 } else {
456 _addRemoveEdit(
457 rangeStartEnd(prefExpression, parExpression.leftParenthesis));
458 _addRemoveEdit(
459 rangeStartEnd(parExpression.rightParenthesis, prefExpression));
460 }
461 _addInsertEdit(isExpression.isOperator.end, "!");
462 // add proposal
463 _addAssist(AssistKind.CONVERT_INTO_IS_NOT, []);
464 }
465
466 void _addProposal_exchangeOperands() {
467 // check that user invokes quick assist on binary expression
468 if (node is! BinaryExpression) {
469 _coverageMarker();
470 return;
471 }
472 BinaryExpression binaryExpression = node as BinaryExpression;
473 // prepare operator position
474 if (!_isOperatorSelected(
475 binaryExpression,
476 selectionOffset,
477 selectionLength)) {
478 _coverageMarker();
479 return;
480 }
481 // add edits
482 {
483 Expression leftOperand = binaryExpression.leftOperand;
484 Expression rightOperand = binaryExpression.rightOperand;
485 // find "wide" enclosing binary expression with same operator
486 while (binaryExpression.parent is BinaryExpression) {
487 BinaryExpression newBinaryExpression =
488 binaryExpression.parent as BinaryExpression;
489 if (newBinaryExpression.operator.type !=
490 binaryExpression.operator.type) {
491 _coverageMarker();
492 break;
493 }
494 binaryExpression = newBinaryExpression;
495 }
496 // exchange parts of "wide" expression parts
497 SourceRange leftRange = rangeStartEnd(binaryExpression, leftOperand);
498 SourceRange rightRange = rangeStartEnd(rightOperand, binaryExpression);
499 _addReplaceEdit(leftRange, _getRangeText(rightRange));
500 _addReplaceEdit(rightRange, _getRangeText(leftRange));
501 }
502 // add proposal
503 _addAssist(AssistKind.EXCHANGE_OPERANDS, []);
504 }
505
506 void _addProposal_importAddShow() {
507 // prepare ImportDirective
508 ImportDirective importDirective =
509 node.getAncestor((node) => node is ImportDirective);
510 if (importDirective == null) {
511 _coverageMarker();
512 return;
513 }
514 // there should be no existing combinators
515 if (importDirective.combinators.isNotEmpty) {
516 _coverageMarker();
517 return;
518 }
519 // prepare whole import namespace
520 ImportElement importElement = importDirective.element;
521 Map<String, Element> namespace = getImportNamespace(importElement);
522 // prepare names of referenced elements (from this import)
523 SplayTreeSet<String> referencedNames = new SplayTreeSet<String>();
524 _SimpleIdentifierRecursiveAstVisitor visitor =
525 new _SimpleIdentifierRecursiveAstVisitor((SimpleIdentifier node) {
526 Element element = node.staticElement;
527 if (namespace[node.name] == element) {
528 referencedNames.add(element.displayName);
529 }
530 });
531 unit.accept(visitor);
532 // ignore if unused
533 if (referencedNames.isEmpty) {
534 _coverageMarker();
535 return;
536 }
537 // prepare change
538 String showCombinator = " show ${StringUtils.join(referencedNames, ", ")}";
539 _addInsertEdit(importDirective.end - 1, showCombinator);
540 // add proposal
541 _addAssist(AssistKind.IMPORT_ADD_SHOW, []);
542 }
543
544 void _addProposal_invertIf() {
545 if (node is! IfStatement) {
546 return;
547 }
548 IfStatement ifStatement = node as IfStatement;
549 Expression condition = ifStatement.condition;
550 // should have both "then" and "else"
551 Statement thenStatement = ifStatement.thenStatement;
552 Statement elseStatement = ifStatement.elseStatement;
553 if (thenStatement == null || elseStatement == null) {
554 return;
555 }
556 // prepare source
557 String invertedCondition = utils.invertCondition(condition);
558 String thenSource = _getNodeText(thenStatement);
559 String elseSource = _getNodeText(elseStatement);
560 // do replacements
561 _addReplaceEdit(rangeNode(condition), invertedCondition);
562 _addReplaceEdit(rangeNode(thenStatement), elseSource);
563 _addReplaceEdit(rangeNode(elseStatement), thenSource);
564 // add proposal
565 _addAssist(AssistKind.INVERT_IF_STATEMENT, []);
566 }
567
568 void _addProposal_joinIfStatementInner() {
569 // climb up condition to the (supposedly) "if" statement
570 AstNode node = this.node;
571 while (node is Expression) {
572 node = node.parent;
573 }
574 // prepare target "if" statement
575 if (node is! IfStatement) {
576 _coverageMarker();
577 return;
578 }
579 IfStatement targetIfStatement = node as IfStatement;
580 if (targetIfStatement.elseStatement != null) {
581 _coverageMarker();
582 return;
583 }
584 // prepare inner "if" statement
585 Statement targetThenStatement = targetIfStatement.thenStatement;
586 Statement innerStatement = getSingleStatement(targetThenStatement);
587 if (innerStatement is! IfStatement) {
588 _coverageMarker();
589 return;
590 }
591 IfStatement innerIfStatement = innerStatement as IfStatement;
592 if (innerIfStatement.elseStatement != null) {
593 _coverageMarker();
594 return;
595 }
596 // prepare environment
597 String prefix = utils.getNodePrefix(targetIfStatement);
598 // merge conditions
599 String condition;
600 {
601 Expression targetCondition = targetIfStatement.condition;
602 Expression innerCondition = innerIfStatement.condition;
603 String targetConditionSource = _getNodeText(targetCondition);
604 String innerConditionSource = _getNodeText(innerCondition);
605 if (_shouldWrapParenthesisBeforeAnd(targetCondition)) {
606 targetConditionSource = "(${targetConditionSource})";
607 }
608 if (_shouldWrapParenthesisBeforeAnd(innerCondition)) {
609 innerConditionSource = "(${innerConditionSource})";
610 }
611 condition = "${targetConditionSource} && ${innerConditionSource}";
612 }
613 // replace target "if" statement
614 {
615 Statement innerThenStatement = innerIfStatement.thenStatement;
616 List<Statement> innerThenStatements = getStatements(innerThenStatement);
617 SourceRange lineRanges =
618 utils.getLinesRangeStatements(innerThenStatements);
619 String oldSource = utils.getRangeText(lineRanges);
620 String newSource = utils.indentSourceLeftRight(oldSource, false);
621 _addReplaceEdit(
622 rangeNode(targetIfStatement),
623 "if ($condition) {${eol}${newSource}${prefix}}");
624 }
625 // done
626 _addAssist(AssistKind.JOIN_IF_WITH_INNER, []);
627 }
628
629 void _addProposal_joinIfStatementOuter() {
630 // climb up condition to the (supposedly) "if" statement
631 AstNode node = this.node;
632 while (node is Expression) {
633 node = node.parent;
634 }
635 // prepare target "if" statement
636 if (node is! IfStatement) {
637 _coverageMarker();
638 return;
639 }
640 IfStatement targetIfStatement = node as IfStatement;
641 if (targetIfStatement.elseStatement != null) {
642 _coverageMarker();
643 return;
644 }
645 // prepare outer "if" statement
646 AstNode parent = targetIfStatement.parent;
647 if (parent is Block) {
648 parent = parent.parent;
649 }
650 if (parent is! IfStatement) {
651 _coverageMarker();
652 return;
653 }
654 IfStatement outerIfStatement = parent as IfStatement;
655 if (outerIfStatement.elseStatement != null) {
656 _coverageMarker();
657 return;
658 }
659 // prepare environment
660 String prefix = utils.getNodePrefix(outerIfStatement);
661 // merge conditions
662 String condition;
663 {
664 Expression targetCondition = targetIfStatement.condition;
665 Expression outerCondition = outerIfStatement.condition;
666 String targetConditionSource = _getNodeText(targetCondition);
667 String outerConditionSource = _getNodeText(outerCondition);
668 if (_shouldWrapParenthesisBeforeAnd(targetCondition)) {
669 targetConditionSource = "(${targetConditionSource})";
670 }
671 if (_shouldWrapParenthesisBeforeAnd(outerCondition)) {
672 outerConditionSource = "(${outerConditionSource})";
673 }
674 condition = "${outerConditionSource} && ${targetConditionSource}";
675 }
676 // replace outer "if" statement
677 {
678 Statement targetThenStatement = targetIfStatement.thenStatement;
679 List<Statement> targetThenStatements = getStatements(targetThenStatement);
680 SourceRange lineRanges =
681 utils.getLinesRangeStatements(targetThenStatements);
682 String oldSource = utils.getRangeText(lineRanges);
683 String newSource = utils.indentSourceLeftRight(oldSource, false);
684 _addReplaceEdit(
685 rangeNode(outerIfStatement),
686 "if ($condition) {${eol}${newSource}${prefix}}");
687 }
688 // done
689 _addAssist(AssistKind.JOIN_IF_WITH_OUTER, []);
690 }
691
692 void _addProposal_joinVariableDeclaration_onAssignment() {
693 // check that node is LHS in assignment
694 if (node is SimpleIdentifier &&
695 node.parent is AssignmentExpression &&
696 (node.parent as AssignmentExpression).leftHandSide == node &&
697 node.parent.parent is ExpressionStatement) {
698 } else {
699 _coverageMarker();
700 return;
701 }
702 AssignmentExpression assignExpression = node.parent as AssignmentExpression;
703 // check that binary expression is assignment
704 if (assignExpression.operator.type != TokenType.EQ) {
705 _coverageMarker();
706 return;
707 }
708 // prepare "declaration" statement
709 Element element = (node as SimpleIdentifier).staticElement;
710 if (element == null) {
711 _coverageMarker();
712 return;
713 }
714 int declOffset = element.nameOffset;
715 AstNode declNode = new NodeLocator.con1(declOffset).searchWithin(unit);
716 if (declNode != null &&
717 declNode.parent is VariableDeclaration &&
718 (declNode.parent as VariableDeclaration).name == declNode &&
719 declNode.parent.parent is VariableDeclarationList &&
720 declNode.parent.parent.parent is VariableDeclarationStatement) {
721 } else {
722 _coverageMarker();
723 return;
724 }
725 VariableDeclaration decl = declNode.parent as VariableDeclaration;
726 VariableDeclarationStatement declStatement =
727 decl.parent.parent as VariableDeclarationStatement;
728 // may be has initializer
729 if (decl.initializer != null) {
730 _coverageMarker();
731 return;
732 }
733 // check that "declaration" statement declared only one variable
734 if (declStatement.variables.variables.length != 1) {
735 _coverageMarker();
736 return;
737 }
738 // check that the "declaration" and "assignment" statements are
739 // parts of the same Block
740 ExpressionStatement assignStatement =
741 node.parent.parent as ExpressionStatement;
742 if (assignStatement.parent is Block &&
743 assignStatement.parent == declStatement.parent) {
744 } else {
745 _coverageMarker();
746 return;
747 }
748 Block block = assignStatement.parent as Block;
749 // check that "declaration" and "assignment" statements are adjacent
750 List<Statement> statements = block.statements;
751 if (statements.indexOf(assignStatement) ==
752 statements.indexOf(declStatement) + 1) {
753 } else {
754 _coverageMarker();
755 return;
756 }
757 // add edits
758 {
759 int assignOffset = assignExpression.operator.offset;
760 _addReplaceEdit(rangeEndStart(declNode, assignOffset), " ");
761 }
762 // add proposal
763 _addAssist(AssistKind.JOIN_VARIABLE_DECLARATION, []);
764 }
765
766 void _addProposal_joinVariableDeclaration_onDeclaration() {
767 // prepare enclosing VariableDeclarationList
768 VariableDeclarationList declList =
769 node.getAncestor((node) => node is VariableDeclarationList);
770 if (declList != null && declList.variables.length == 1) {
771 } else {
772 _coverageMarker();
773 return;
774 }
775 VariableDeclaration decl = declList.variables[0];
776 // already initialized
777 if (decl.initializer != null) {
778 _coverageMarker();
779 return;
780 }
781 // prepare VariableDeclarationStatement in Block
782 if (declList.parent is VariableDeclarationStatement &&
783 declList.parent.parent is Block) {
784 } else {
785 _coverageMarker();
786 return;
787 }
788 VariableDeclarationStatement declStatement =
789 declList.parent as VariableDeclarationStatement;
790 Block block = declStatement.parent as Block;
791 List<Statement> statements = block.statements;
792 // prepare assignment
793 AssignmentExpression assignExpression;
794 {
795 // declaration should not be last Statement
796 int declIndex = statements.indexOf(declStatement);
797 if (declIndex < statements.length - 1) {
798 } else {
799 _coverageMarker();
800 return;
801 }
802 // next Statement should be assignment
803 Statement assignStatement = statements[declIndex + 1];
804 if (assignStatement is ExpressionStatement) {
805 } else {
806 _coverageMarker();
807 return;
808 }
809 ExpressionStatement expressionStatement =
810 assignStatement as ExpressionStatement;
811 // expression should be assignment
812 if (expressionStatement.expression is AssignmentExpression) {
813 } else {
814 _coverageMarker();
815 return;
816 }
817 assignExpression = expressionStatement.expression as AssignmentExpression;
818 }
819 // check that pure assignment
820 if (assignExpression.operator.type != TokenType.EQ) {
821 _coverageMarker();
822 return;
823 }
824 // add edits
825 {
826 int assignOffset = assignExpression.operator.offset;
827 _addReplaceEdit(rangeEndStart(decl.name, assignOffset), " ");
828 }
829 // add proposal
830 _addAssist(AssistKind.JOIN_VARIABLE_DECLARATION, []);
831 }
832
833 void _addProposal_removeTypeAnnotation() {
834 AstNode typeStart = null;
835 AstNode typeEnd = null;
836 // try top-level variable
837 {
838 TopLevelVariableDeclaration declaration =
839 node.getAncestor((node) => node is TopLevelVariableDeclaration);
840 if (declaration != null) {
841 TypeName typeNode = declaration.variables.type;
842 if (typeNode != null) {
843 VariableDeclaration field = declaration.variables.variables[0];
844 typeStart = declaration;
845 typeEnd = field;
846 }
847 }
848 }
849 // try class field
850 {
851 FieldDeclaration fieldDeclaration =
852 node.getAncestor((node) => node is FieldDeclaration);
853 if (fieldDeclaration != null) {
854 TypeName typeNode = fieldDeclaration.fields.type;
855 if (typeNode != null) {
856 VariableDeclaration field = fieldDeclaration.fields.variables[0];
857 typeStart = fieldDeclaration;
858 typeEnd = field;
859 }
860 }
861 }
862 // try local variable
863 {
864 VariableDeclarationStatement statement =
865 node.getAncestor((node) => node is VariableDeclarationStatement);
866 if (statement != null) {
867 TypeName typeNode = statement.variables.type;
868 if (typeNode != null) {
869 VariableDeclaration variable = statement.variables.variables[0];
870 typeStart = typeNode;
871 typeEnd = variable;
872 }
873 }
874 }
875 // add edit
876 if (typeStart != null && typeEnd != null) {
877 SourceRange typeRange = rangeStartStart(typeStart, typeEnd);
878 _addReplaceEdit(typeRange, "var ");
879 }
880 // add proposal
881 _addAssist(AssistKind.REMOVE_TYPE_ANNOTATION, []);
882 }
883
884 void _addProposal_replaceConditionalWithIfElse() {
885 ConditionalExpression conditional = null;
886 // may be on Statement with Conditional
887 Statement statement = node.getAncestor((node) => node is Statement);
888 if (statement == null) {
889 _coverageMarker();
890 return;
891 }
892 // variable declaration
893 bool inVariable = false;
894 if (statement is VariableDeclarationStatement) {
895 VariableDeclarationStatement variableStatement = statement;
896 for (VariableDeclaration variable in
897 variableStatement.variables.variables) {
898 if (variable.initializer is ConditionalExpression) {
899 conditional = variable.initializer as ConditionalExpression;
900 inVariable = true;
901 break;
902 }
903 }
904 }
905 // assignment
906 bool inAssignment = false;
907 if (statement is ExpressionStatement) {
908 ExpressionStatement exprStmt = statement;
909 if (exprStmt.expression is AssignmentExpression) {
910 AssignmentExpression assignment =
911 exprStmt.expression as AssignmentExpression;
912 if (assignment.operator.type == TokenType.EQ &&
913 assignment.rightHandSide is ConditionalExpression) {
914 conditional = assignment.rightHandSide as ConditionalExpression;
915 inAssignment = true;
916 }
917 }
918 }
919 // return
920 bool inReturn = false;
921 if (statement is ReturnStatement) {
922 ReturnStatement returnStatement = statement;
923 if (returnStatement.expression is ConditionalExpression) {
924 conditional = returnStatement.expression as ConditionalExpression;
925 inReturn = true;
926 }
927 }
928 // prepare environment
929 String indent = utils.getIndent(1);
930 String prefix = utils.getNodePrefix(statement);
931 // Type v = Conditional;
932 if (inVariable) {
933 VariableDeclaration variable = conditional.parent as VariableDeclaration;
934 _addRemoveEdit(rangeEndEnd(variable.name, conditional));
935 String conditionSrc = _getNodeText(conditional.condition);
936 String thenSrc = _getNodeText(conditional.thenExpression);
937 String elseSrc = _getNodeText(conditional.elseExpression);
938 String name = variable.name.name;
939 String src = eol;
940 src += prefix + 'if ($conditionSrc) {' + eol;
941 src += prefix + indent + '$name = $thenSrc;' + eol;
942 src += prefix + '} else {' + eol;
943 src += prefix + indent + '$name = $elseSrc;' + eol;
944 src += prefix + '}';
945 _addReplaceEdit(rangeEndLength(statement, 0), src);
946 }
947 // v = Conditional;
948 if (inAssignment) {
949 AssignmentExpression assignment =
950 conditional.parent as AssignmentExpression;
951 Expression leftSide = assignment.leftHandSide;
952 String conditionSrc = _getNodeText(conditional.condition);
953 String thenSrc = _getNodeText(conditional.thenExpression);
954 String elseSrc = _getNodeText(conditional.elseExpression);
955 String name = _getNodeText(leftSide);
956 String src = '';
957 src += 'if ($conditionSrc) {' + eol;
958 src += prefix + indent + '$name = $thenSrc;' + eol;
959 src += prefix + '} else {' + eol;
960 src += prefix + indent + '$name = $elseSrc;' + eol;
961 src += prefix + '}';
962 _addReplaceEdit(rangeNode(statement), src);
963 }
964 // return Conditional;
965 if (inReturn) {
966 String conditionSrc = _getNodeText(conditional.condition);
967 String thenSrc = _getNodeText(conditional.thenExpression);
968 String elseSrc = _getNodeText(conditional.elseExpression);
969 String src = '';
970 src += 'if ($conditionSrc) {' + eol;
971 src += prefix + indent + 'return $thenSrc;' + eol;
972 src += prefix + '} else {' + eol;
973 src += prefix + indent + 'return $elseSrc;' + eol;
974 src += prefix + '}';
975 _addReplaceEdit(rangeNode(statement), src);
976 }
977 // add proposal
978 _addAssist(AssistKind.REPLACE_CONDITIONAL_WITH_IF_ELSE, []);
979 }
980
981 void _addProposal_replaceIfElseWithConditional() {
982 // should be "if"
983 if (node is! IfStatement) {
984 _coverageMarker();
985 return;
986 }
987 IfStatement ifStatement = node as IfStatement;
988 // single then/else statements
989 Statement thenStatement = getSingleStatement(ifStatement.thenStatement);
990 Statement elseStatement = getSingleStatement(ifStatement.elseStatement);
991 if (thenStatement == null || elseStatement == null) {
992 _coverageMarker();
993 return;
994 }
995 // returns
996 if (thenStatement is ReturnStatement || elseStatement is ReturnStatement) {
997 ReturnStatement thenReturn = thenStatement as ReturnStatement;
998 ReturnStatement elseReturn = elseStatement as ReturnStatement;
999 String conditionSrc = _getNodeText(ifStatement.condition);
1000 String theSrc = _getNodeText(thenReturn.expression);
1001 String elseSrc = _getNodeText(elseReturn.expression);
1002 _addReplaceEdit(
1003 rangeNode(ifStatement),
1004 'return $conditionSrc ? $theSrc : $elseSrc;');
1005 }
1006 // assignments -> v = Conditional;
1007 if (thenStatement is ExpressionStatement &&
1008 elseStatement is ExpressionStatement) {
1009 Expression thenExpression = thenStatement.expression;
1010 Expression elseExpression = elseStatement.expression;
1011 if (thenExpression is AssignmentExpression &&
1012 elseExpression is AssignmentExpression) {
1013 AssignmentExpression thenAssignment = thenExpression;
1014 AssignmentExpression elseAssignment = elseExpression;
1015 String thenTarget = _getNodeText(thenAssignment.leftHandSide);
1016 String elseTarget = _getNodeText(elseAssignment.leftHandSide);
1017 if (thenAssignment.operator.type == TokenType.EQ &&
1018 elseAssignment.operator.type == TokenType.EQ &&
1019 StringUtils.equals(thenTarget, elseTarget)) {
1020 String conditionSrc = _getNodeText(ifStatement.condition);
1021 String theSrc = _getNodeText(thenAssignment.rightHandSide);
1022 String elseSrc = _getNodeText(elseAssignment.rightHandSide);
1023 _addReplaceEdit(
1024 rangeNode(ifStatement),
1025 '$thenTarget = $conditionSrc ? $theSrc : $elseSrc;');
1026 }
1027 }
1028 }
1029 // add proposal
1030 _addAssist(AssistKind.REPLACE_IF_ELSE_WITH_CONDITIONAL, []);
1031 }
1032
1033 void _addProposal_splitAndCondition() {
1034 // check that user invokes quick assist on binary expression
1035 if (node is! BinaryExpression) {
1036 _coverageMarker();
1037 return;
1038 }
1039 BinaryExpression binaryExpression = node as BinaryExpression;
1040 // prepare operator position
1041 if (!_isOperatorSelected(
1042 binaryExpression,
1043 selectionOffset,
1044 selectionLength)) {
1045 _coverageMarker();
1046 return;
1047 }
1048 // should be &&
1049 if (binaryExpression.operator.type != TokenType.AMPERSAND_AMPERSAND) {
1050 _coverageMarker();
1051 return;
1052 }
1053 // prepare "if"
1054 Statement statement = node.getAncestor((node) => node is Statement);
1055 if (statement is! IfStatement) {
1056 _coverageMarker();
1057 return;
1058 }
1059 IfStatement ifStatement = statement as IfStatement;
1060 // check that binary expression is part of first level && condition of "if"
1061 BinaryExpression condition = binaryExpression;
1062 while (condition.parent is BinaryExpression &&
1063 (condition.parent as BinaryExpression).operator.type ==
1064 TokenType.AMPERSAND_AMPERSAND) {
1065 condition = condition.parent as BinaryExpression;
1066 }
1067 if (ifStatement.condition != condition) {
1068 _coverageMarker();
1069 return;
1070 }
1071 // prepare environment
1072 String prefix = utils.getNodePrefix(ifStatement);
1073 String indent = utils.getIndent(1);
1074 // prepare "rightCondition"
1075 String rightConditionSource;
1076 {
1077 SourceRange rightConditionRange =
1078 rangeStartEnd(binaryExpression.rightOperand, condition);
1079 rightConditionSource = _getRangeText(rightConditionRange);
1080 }
1081 // remove "&& rightCondition"
1082 _addRemoveEdit(rangeEndEnd(binaryExpression.leftOperand, condition));
1083 // update "then" statement
1084 Statement thenStatement = ifStatement.thenStatement;
1085 Statement elseStatement = ifStatement.elseStatement;
1086 if (thenStatement is Block) {
1087 Block thenBlock = thenStatement;
1088 SourceRange thenBlockRange = rangeNode(thenBlock);
1089 // insert inner "if" with right part of "condition"
1090 {
1091 String source =
1092 "${eol}${prefix}${indent}if (${rightConditionSource}) {";
1093 int thenBlockInsideOffset = thenBlockRange.offset + 1;
1094 _addInsertEdit(thenBlockInsideOffset, source);
1095 }
1096 // insert closing "}" for inner "if"
1097 {
1098 int thenBlockEnd = thenBlockRange.end;
1099 String source = "${indent}}";
1100 // may be move "else" statements
1101 if (elseStatement != null) {
1102 List<Statement> elseStatements = getStatements(elseStatement);
1103 SourceRange elseLinesRange =
1104 utils.getLinesRangeStatements(elseStatements);
1105 String elseIndentOld = "${prefix}${indent}";
1106 String elseIndentNew = "${elseIndentOld}${indent}";
1107 String newElseSource =
1108 utils.replaceSourceRangeIndent(elseLinesRange, elseIndentOld, else IndentNew);
1109 // append "else" block
1110 source += " else {${eol}";
1111 source += newElseSource;
1112 source += "${prefix}${indent}}";
1113 // remove old "else" range
1114 _addRemoveEdit(rangeStartEnd(thenBlockEnd, elseStatement));
1115 }
1116 // insert before outer "then" block "}"
1117 source += "${eol}${prefix}";
1118 _addInsertEdit(thenBlockEnd - 1, source);
1119 }
1120 } else {
1121 // insert inner "if" with right part of "condition"
1122 {
1123 String source = "${eol}${prefix}${indent}if (${rightConditionSource})";
1124 _addInsertEdit(ifStatement.rightParenthesis.offset + 1, source);
1125 }
1126 // indent "else" statements to correspond inner "if"
1127 if (elseStatement != null) {
1128 SourceRange elseRange =
1129 rangeStartEnd(ifStatement.elseKeyword.offset, elseStatement);
1130 SourceRange elseLinesRange = utils.getLinesRange(elseRange);
1131 String elseIndentOld = prefix;
1132 String elseIndentNew = "${elseIndentOld}${indent}";
1133 edits.add(
1134 utils.createIndentEdit(elseLinesRange, elseIndentOld, elseIndentNew) );
1135 }
1136 }
1137 // indent "then" statements to correspond inner "if"
1138 {
1139 List<Statement> thenStatements = getStatements(thenStatement);
1140 SourceRange linesRange = utils.getLinesRangeStatements(thenStatements);
1141 String thenIndentOld = "${prefix}${indent}";
1142 String thenIndentNew = "${thenIndentOld}${indent}";
1143 edits.add(
1144 utils.createIndentEdit(linesRange, thenIndentOld, thenIndentNew));
1145 }
1146 // add proposal
1147 _addAssist(AssistKind.SPLIT_AND_CONDITION, []);
1148 }
1149
1150 void _addProposal_splitVariableDeclaration() {
1151 // prepare DartVariableStatement, should be part of Block
1152 VariableDeclarationStatement statement =
1153 node.getAncestor((node) => node is VariableDeclarationStatement);
1154 if (statement != null && statement.parent is Block) {
1155 } else {
1156 _coverageMarker();
1157 return;
1158 }
1159 // check that statement declares single variable
1160 List<VariableDeclaration> variables = statement.variables.variables;
1161 if (variables.length != 1) {
1162 _coverageMarker();
1163 return;
1164 }
1165 VariableDeclaration variable = variables[0];
1166 // prepare initializer
1167 Expression initializer = variable.initializer;
1168 if (initializer == null) {
1169 _coverageMarker();
1170 return;
1171 }
1172 // remove initializer value
1173 _addRemoveEdit(rangeEndStart(variable.name, statement.semicolon));
1174 // add assignment statement
1175 String indent = utils.getNodePrefix(statement);
1176 String name = variable.name.name;
1177 String initSrc = _getNodeText(initializer);
1178 SourceRange assignRange = rangeEndLength(statement, 0);
1179 _addReplaceEdit(assignRange, eol + indent + name + ' = ' + initSrc + ';');
1180 // add proposal
1181 _addAssist(AssistKind.SPLIT_VARIABLE_DECLARATION, []);
1182 }
1183
1184 void _addProposal_surroundWith() {
1185 // prepare selected statements
1186 List<Statement> selectedStatements;
1187 {
1188 SourceRange selection =
1189 rangeStartLength(selectionOffset, selectionLength);
1190 StatementAnalyzer selectionAnalyzer =
1191 new StatementAnalyzer(unit, selection);
1192 unit.accept(selectionAnalyzer);
1193 List<AstNode> selectedNodes = selectionAnalyzer.selectedNodes;
1194 // convert nodes to statements
1195 selectedStatements = [];
1196 for (AstNode selectedNode in selectedNodes) {
1197 if (selectedNode is Statement) {
1198 selectedStatements.add(selectedNode);
1199 }
1200 }
1201 // we want only statements
1202 if (selectedStatements.isEmpty ||
1203 selectedStatements.length != selectedNodes.length) {
1204 return;
1205 }
1206 }
1207 // prepare statement information
1208 Statement firstStatement = selectedStatements[0];
1209 Statement lastStatement = selectedStatements[selectedStatements.length - 1];
1210 SourceRange statementsRange =
1211 utils.getLinesRangeStatements(selectedStatements);
1212 // prepare environment
1213 String indentOld = utils.getNodePrefix(firstStatement);
1214 String indentNew = "${indentOld}${utils.getIndent(1)}";
1215 // "block"
1216 {
1217 _addInsertEdit(statementsRange.offset, "${indentOld}{${eol}");
1218 {
1219 Edit edit =
1220 utils.createIndentEdit(statementsRange, indentOld, indentNew);
1221 edits.add(edit);
1222 }
1223 _addInsertEdit(statementsRange.end, "${indentOld}}${eol}");
1224 exitPosition = _newPosition(lastStatement.end);
1225 // add proposal
1226 _addAssist(AssistKind.SURROUND_WITH_BLOCK, []);
1227 }
1228 // "if"
1229 {
1230 {
1231 int offset = statementsRange.offset;
1232 SourceBuilder sb = new SourceBuilder(file, offset);
1233 sb.append(indentOld);
1234 sb.append("if (");
1235 {
1236 sb.startPosition("CONDITION");
1237 sb.append("condition");
1238 sb.endPosition();
1239 }
1240 sb.append(") {");
1241 sb.append(eol);
1242 _insertBuilder(sb);
1243 }
1244 {
1245 Edit edit =
1246 utils.createIndentEdit(statementsRange, indentOld, indentNew);
1247 edits.add(edit);
1248 }
1249 _addInsertEdit(statementsRange.end, "${indentOld}}${eol}");
1250 exitPosition = _newPosition(lastStatement.end);
1251 // add proposal
1252 _addAssist(AssistKind.SURROUND_WITH_IF, []);
1253 }
1254 // "while"
1255 {
1256 {
1257 int offset = statementsRange.offset;
1258 SourceBuilder sb = new SourceBuilder(file, offset);
1259 sb.append(indentOld);
1260 sb.append("while (");
1261 {
1262 sb.startPosition("CONDITION");
1263 sb.append("condition");
1264 sb.endPosition();
1265 }
1266 sb.append(") {");
1267 sb.append(eol);
1268 _insertBuilder(sb);
1269 }
1270 {
1271 Edit edit =
1272 utils.createIndentEdit(statementsRange, indentOld, indentNew);
1273 edits.add(edit);
1274 }
1275 _addInsertEdit(statementsRange.end, "${indentOld}}${eol}");
1276 exitPosition = _newPosition(lastStatement.end);
1277 // add proposal
1278 _addAssist(AssistKind.SURROUND_WITH_WHILE, []);
1279 }
1280 // "for-in"
1281 {
1282 {
1283 int offset = statementsRange.offset;
1284 SourceBuilder sb = new SourceBuilder(file, offset);
1285 sb.append(indentOld);
1286 sb.append("for (var ");
1287 {
1288 sb.startPosition("NAME");
1289 sb.append("item");
1290 sb.endPosition();
1291 }
1292 sb.append(" in ");
1293 {
1294 sb.startPosition("ITERABLE");
1295 sb.append("iterable");
1296 sb.endPosition();
1297 }
1298 sb.append(") {");
1299 sb.append(eol);
1300 _insertBuilder(sb);
1301 }
1302 {
1303 Edit edit =
1304 utils.createIndentEdit(statementsRange, indentOld, indentNew);
1305 edits.add(edit);
1306 }
1307 _addInsertEdit(statementsRange.end, "${indentOld}}${eol}");
1308 exitPosition = _newPosition(lastStatement.end);
1309 // add proposal
1310 _addAssist(AssistKind.SURROUND_WITH_FOR_IN, []);
1311 }
1312 // "for"
1313 {
1314 {
1315 int offset = statementsRange.offset;
1316 SourceBuilder sb = new SourceBuilder(file, offset);
1317 sb.append(indentOld);
1318 sb.append("for (var ");
1319 {
1320 sb.startPosition("VAR");
1321 sb.append("v");
1322 sb.endPosition();
1323 }
1324 sb.append(" = ");
1325 {
1326 sb.startPosition("INIT");
1327 sb.append("init");
1328 sb.endPosition();
1329 }
1330 sb.append("; ");
1331 {
1332 sb.startPosition("CONDITION");
1333 sb.append("condition");
1334 sb.endPosition();
1335 }
1336 sb.append("; ");
1337 {
1338 sb.startPosition("INCREMENT");
1339 sb.append("increment");
1340 sb.endPosition();
1341 }
1342 sb.append(") {");
1343 sb.append(eol);
1344 _insertBuilder(sb);
1345 }
1346 {
1347 Edit edit =
1348 utils.createIndentEdit(statementsRange, indentOld, indentNew);
1349 edits.add(edit);
1350 }
1351 _addInsertEdit(statementsRange.end, "${indentOld}}${eol}");
1352 exitPosition = _newPosition(lastStatement.end);
1353 // add proposal
1354 _addAssist(AssistKind.SURROUND_WITH_FOR, []);
1355 }
1356 // "do-while"
1357 {
1358 _addInsertEdit(statementsRange.offset, "${indentOld}do {${eol}");
1359 {
1360 Edit edit =
1361 utils.createIndentEdit(statementsRange, indentOld, indentNew);
1362 edits.add(edit);
1363 }
1364 {
1365 int offset = statementsRange.end;
1366 SourceBuilder sb = new SourceBuilder(file, offset);
1367 sb.append(indentOld);
1368 sb.append("} while (");
1369 {
1370 sb.startPosition("CONDITION");
1371 sb.append("condition");
1372 sb.endPosition();
1373 }
1374 sb.append(");");
1375 sb.append(eol);
1376 _insertBuilder(sb);
1377 }
1378 exitPosition = _newPosition(lastStatement.end);
1379 // add proposal
1380 _addAssist(AssistKind.SURROUND_WITH_DO_WHILE, []);
1381 }
1382 // "try-catch"
1383 {
1384 _addInsertEdit(statementsRange.offset, "${indentOld}try {${eol}");
1385 {
1386 Edit edit =
1387 utils.createIndentEdit(statementsRange, indentOld, indentNew);
1388 edits.add(edit);
1389 }
1390 {
1391 int offset = statementsRange.end;
1392 SourceBuilder sb = new SourceBuilder(file, offset);
1393 sb.append(indentOld);
1394 sb.append("} on ");
1395 {
1396 sb.startPosition("EXCEPTION_TYPE");
1397 sb.append("Exception");
1398 sb.endPosition();
1399 }
1400 sb.append(" catch (");
1401 {
1402 sb.startPosition("EXCEPTION_VAR");
1403 sb.append("e");
1404 sb.endPosition();
1405 }
1406 sb.append(") {");
1407 sb.append(eol);
1408 //
1409 sb.append(indentNew);
1410 {
1411 sb.startPosition("CATCH");
1412 sb.append("// TODO");
1413 sb.endPosition();
1414 sb.setExitOffset();
1415 }
1416 sb.append(eol);
1417 //
1418 sb.append(indentOld);
1419 sb.append("}");
1420 sb.append(eol);
1421 //
1422 _insertBuilder(sb);
1423 exitPosition = _newPosition(sb.exitOffset);
1424 }
1425 // add proposal
1426 _addAssist(AssistKind.SURROUND_WITH_TRY_CATCH, []);
1427 }
1428 // "try-finally"
1429 {
1430 _addInsertEdit(statementsRange.offset, "${indentOld}try {${eol}");
1431 {
1432 Edit edit =
1433 utils.createIndentEdit(statementsRange, indentOld, indentNew);
1434 edits.add(edit);
1435 }
1436 {
1437 int offset = statementsRange.end;
1438 SourceBuilder sb = new SourceBuilder(file, offset);
1439 //
1440 sb.append(indentOld);
1441 sb.append("} finally {");
1442 sb.append(eol);
1443 //
1444 sb.append(indentNew);
1445 {
1446 sb.startPosition("FINALLY");
1447 sb.append("// TODO");
1448 sb.endPosition();
1449 }
1450 sb.setExitOffset();
1451 sb.append(eol);
1452 //
1453 sb.append(indentOld);
1454 sb.append("}");
1455 sb.append(eol);
1456 //
1457 _insertBuilder(sb);
1458 exitPosition = _newPosition(sb.exitOffset);
1459 }
1460 // add proposal
1461 _addAssist(AssistKind.SURROUND_WITH_TRY_FINALLY, []);
1462 }
1463 }
1464
1465 /**
1466 * Adds a new [Edit] to [edits].
1467 */
1468 void _addRemoveEdit(SourceRange range) {
1469 _addReplaceEdit(range, '');
1470 }
1471
1472 /**
1473 * Adds a new [Edit] to [edits].
1474 */
1475 void _addReplaceEdit(SourceRange range, String text) {
1476 Edit edit = new Edit(range.offset, range.length, text);
1477 edits.add(edit);
1478 }
1479
1480 /**
1481 * Returns an existing or just added [LinkedEditGroup] with [groupId].
1482 */
1483 LinkedEditGroup _getLinkedPosition(String groupId) {
1484 LinkedEditGroup group = linkedPositionGroups[groupId];
1485 if (group == null) {
1486 group = new LinkedEditGroup(groupId);
1487 linkedPositionGroups[groupId] = group;
1488 }
1489 return group;
1490 }
1491
1492 /**
1493 * Returns the text of the given node in the unit.
1494 */
1495 String _getNodeText(AstNode node) {
1496 return utils.getNodeText(node);
1497 }
1498
1499 /**
1500 * Returns the text of the given range in the unit.
1501 */
1502 String _getRangeText(SourceRange range) {
1503 return utils.getRangeText(range);
1504 }
1505
1506 /**
1507 * Inserts the given [SourceBuilder] at its offset.
1508 */
1509 void _insertBuilder(SourceBuilder builder) {
1510 String text = builder.toString();
1511 _addInsertEdit(builder.offset, text);
1512 // add linked positions
1513 builder.linkedPositionGroups.forEach((LinkedEditGroup group) {
1514 LinkedEditGroup fixGroup = _getLinkedPosition(group.id);
1515 group.positions.forEach((Position position) {
1516 fixGroup.addPosition(position, group.length);
1517 });
1518 group.suggestions.forEach((LinkedEditSuggestion suggestion) {
1519 fixGroup.addSuggestion(suggestion);
1520 });
1521 });
1522 }
1523
1524 Position _newPosition(int offset) {
1525 return new Position(file, offset);
1526 }
1527
1528 /**
1529 * This method does nothing, but we invoke it in places where Dart VM
1530 * coverage agent fails to provide coverage information - such as almost
1531 * all "return" statements.
1532 *
1533 * https://code.google.com/p/dart/issues/detail?id=19912
1534 */
1535 static void _coverageMarker() {
1536 }
1537
1538 /**
1539 * Returns `true` if the selection covers an operator of the given
1540 * [BinaryExpression].
1541 */
1542 static bool _isOperatorSelected(BinaryExpression binaryExpression, int offset,
1543 int length) {
1544 AstNode left = binaryExpression.leftOperand;
1545 AstNode right = binaryExpression.rightOperand;
1546 // between the nodes
1547 if (offset >= left.endToken.end && offset + length <= right.offset) {
1548 _coverageMarker();
1549 return true;
1550 }
1551 // or exactly select the node (but not with infix expressions)
1552 if (offset == left.offset && offset + length == right.endToken.end) {
1553 if (left is BinaryExpression || right is BinaryExpression) {
1554 _coverageMarker();
1555 return false;
1556 }
1557 _coverageMarker();
1558 return true;
1559 }
1560 // invalid selection (part of node, etc)
1561 _coverageMarker();
1562 return false;
1563 }
1564
1565 /**
1566 * Checks if the given [Expression] should be wrapped with parenthesis when we
1567 * want to use it as operand of a logical `and` expression.
1568 */
1569 static bool _shouldWrapParenthesisBeforeAnd(Expression expr) {
1570 if (expr is BinaryExpression) {
1571 BinaryExpression binary = expr;
1572 int precedence = binary.operator.type.precedence;
1573 return precedence < TokenClass.LOGICAL_AND_OPERATOR.precedence;
1574 }
1575 return false;
1576 }
1577 }
1578
1579
1580 class _SimpleIdentifierRecursiveAstVisitor extends RecursiveAstVisitor {
1581 final _SimpleIdentifierVisitor visitor;
1582
1583 _SimpleIdentifierRecursiveAstVisitor(this.visitor);
1584
1585 @override
1586 visitSimpleIdentifier(SimpleIdentifier node) {
1587 visitor(node);
1588 }
1589 }
OLDNEW
« no previous file with comments | « pkg/analysis_services/lib/src/completion/local_computer.dart ('k') | pkg/analysis_services/lib/src/correction/fix.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698