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

Side by Side Diff: pkg/analysis_server/lib/src/services/refactoring/extract_method.dart

Issue 2907213002: [Extract Method] When no selection, check for implicitly selected closure. (Closed)
Patch Set: Created 3 years, 6 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
« no previous file with comments | « no previous file | pkg/analysis_server/test/services/refactoring/extract_method_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 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 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 import 'dart:async'; 5 import 'dart:async';
6 6
7 import 'package:analysis_server/src/protocol_server.dart' hide Element; 7 import 'package:analysis_server/src/protocol_server.dart' hide Element;
8 import 'package:analysis_server/src/services/correction/name_suggestion.dart'; 8 import 'package:analysis_server/src/services/correction/name_suggestion.dart';
9 import 'package:analysis_server/src/services/correction/selection_analyzer.dart' ; 9 import 'package:analysis_server/src/services/correction/selection_analyzer.dart' ;
10 import 'package:analysis_server/src/services/correction/statement_analyzer.dart' ; 10 import 'package:analysis_server/src/services/correction/statement_analyzer.dart' ;
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 if (_staticContext) { 316 if (_staticContext) {
317 annotations = 'static '; 317 annotations = 'static ';
318 } 318 }
319 } 319 }
320 // prepare declaration source 320 // prepare declaration source
321 String declarationSource = null; 321 String declarationSource = null;
322 { 322 {
323 String returnExpressionSource = _getMethodBodySource(); 323 String returnExpressionSource = _getMethodBodySource();
324 // closure 324 // closure
325 if (_selectionFunctionExpression != null) { 325 if (_selectionFunctionExpression != null) {
326 declarationSource = '$name$returnExpressionSource'; 326 String returnTypeCode = _getExpectedClosureReturnTypeCode();
327 declarationSource = '$returnTypeCode$name$returnExpressionSource';
327 if (_selectionFunctionExpression.body is ExpressionFunctionBody) { 328 if (_selectionFunctionExpression.body is ExpressionFunctionBody) {
328 declarationSource += ';'; 329 declarationSource += ';';
329 } 330 }
330 } 331 }
331 // optional 'async' body modifier 332 // optional 'async' body modifier
332 String asyncKeyword = _hasAwait ? ' async' : ''; 333 String asyncKeyword = _hasAwait ? ' async' : '';
333 // expression 334 // expression
334 if (_selectionExpression != null) { 335 if (_selectionExpression != null) {
335 // add return type 336 // add return type
336 if (returnType.isNotEmpty) { 337 if (returnType.isNotEmpty) {
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 } 422 }
422 // OK 423 // OK
423 return new Future<RefactoringStatus>.value(result); 424 return new Future<RefactoringStatus>.value(result);
424 } 425 }
425 426
426 /** 427 /**
427 * Checks if [selectionRange] selects [Expression] which can be extracted, and 428 * Checks if [selectionRange] selects [Expression] which can be extracted, and
428 * location of this [DartExpression] in AST allows extracting. 429 * location of this [DartExpression] in AST allows extracting.
429 */ 430 */
430 RefactoringStatus _checkSelection() { 431 RefactoringStatus _checkSelection() {
432 // Check for implicitly selected closure.
433 {
434 FunctionExpression function = _findFunctionExpression();
435 if (function != null) {
436 _selectionFunctionExpression = function;
437 selectionRange = range.node(function);
438 _parentMember = getEnclosingClassOrUnitMember(function);
439 return new RefactoringStatus();
440 }
441 }
442
431 _ExtractMethodAnalyzer selectionAnalyzer = 443 _ExtractMethodAnalyzer selectionAnalyzer =
432 new _ExtractMethodAnalyzer(unit, selectionRange); 444 new _ExtractMethodAnalyzer(unit, selectionRange);
433 unit.accept(selectionAnalyzer); 445 unit.accept(selectionAnalyzer);
434 // may be fatal error 446 // May be a fatal error.
435 { 447 {
436 RefactoringStatus status = selectionAnalyzer.status; 448 RefactoringStatus status = selectionAnalyzer.status;
437 if (status.hasFatalError) { 449 if (status.hasFatalError) {
438 return status; 450 return status;
439 } 451 }
440 } 452 }
441 // check selected nodes
442 List<AstNode> selectedNodes = selectionAnalyzer.selectedNodes; 453 List<AstNode> selectedNodes = selectionAnalyzer.selectedNodes;
454
455 // Check selected nodes.
443 if (!selectedNodes.isEmpty) { 456 if (!selectedNodes.isEmpty) {
444 AstNode coveringNode = selectionAnalyzer.coveringNode; 457 AstNode selectedNode = selectedNodes.first;
445 _parentMember = getEnclosingClassOrUnitMember(coveringNode); 458 _parentMember = getEnclosingClassOrUnitMember(selectedNode);
446 // single expression selected 459 // single expression selected
447 if (selectedNodes.length == 1 && 460 if (selectedNodes.length == 1) {
448 !utils.selectionIncludesNonWhitespaceOutsideNode( 461 if (!utils.selectionIncludesNonWhitespaceOutsideNode(
449 selectionRange, selectionAnalyzer.firstSelectedNode)) { 462 selectionRange, selectedNode)) {
450 AstNode selectedNode = selectionAnalyzer.firstSelectedNode; 463 if (selectedNode is Expression) {
451 if (selectedNode is Expression) { 464 _selectionExpression = selectedNode;
452 _selectionExpression = selectedNode; 465 // additional check for closure
453 // additional check for closure 466 if (_selectionExpression is FunctionExpression) {
454 if (_selectionExpression is FunctionExpression) { 467 _selectionFunctionExpression =
455 _selectionFunctionExpression = 468 _selectionExpression as FunctionExpression;
456 _selectionExpression as FunctionExpression; 469 _selectionExpression = null;
457 _selectionExpression = null; 470 }
471 // OK
472 return new RefactoringStatus();
458 } 473 }
459 // OK
460 return new RefactoringStatus();
461 } 474 }
462 } 475 }
463 // statements selected 476 // statements selected
464 { 477 {
465 List<Statement> selectedStatements = []; 478 List<Statement> selectedStatements = [];
466 for (AstNode selectedNode in selectedNodes) { 479 for (AstNode selectedNode in selectedNodes) {
467 if (selectedNode is Statement) { 480 if (selectedNode is Statement) {
468 selectedStatements.add(selectedNode); 481 selectedStatements.add(selectedNode);
469 } 482 }
470 } 483 }
(...skipping 28 matching lines...) Expand all
499 } 512 }
500 // doesn't return a value 513 // doesn't return a value
501 if (_selectionStatements != null) { 514 if (_selectionStatements != null) {
502 return returnType != 'void'; 515 return returnType != 'void';
503 } 516 }
504 // OK 517 // OK
505 return true; 518 return true;
506 } 519 }
507 520
508 /** 521 /**
522 * If the [selectionRange] is associated with a [FunctionExpression], return
523 * this [FunctionExpression].
524 */
525 FunctionExpression _findFunctionExpression() {
526 if (selectionRange.length != 0) {
527 return null;
528 }
529 int offset = selectionRange.offset;
530 AstNode node = new NodeLocator2(offset, offset).searchWithin(unit);
531
532 // Check for the parameter list of a FunctionExpression.
533 {
534 FunctionExpression function =
535 node?.getAncestor((n) => n is FunctionExpression);
536 if (function != null &&
537 function.parameters != null &&
538 range.node(function.parameters).contains(offset)) {
539 return function;
540 }
541 }
542
543 // Check for the name of the named argument with the closure expression.
544 if (node is SimpleIdentifier &&
545 node.parent is Label &&
546 node.parent.parent is NamedExpression) {
547 NamedExpression namedExpression = node.parent.parent;
548 Expression expression = namedExpression.expression;
549 if (expression is FunctionExpression) {
550 return expression;
551 }
552 }
553
554 return null;
555 }
556
557 /**
558 * If the selected closure (i.e. [_selectionFunctionExpression]) is an
559 * argument for a function typed parameter (as it should be), and the
560 * function type has the return type specified, return this return type's
561 * code. Otherwise return the empty string.
562 */
563 String _getExpectedClosureReturnTypeCode() {
564 Expression argument = _selectionFunctionExpression;
565 if (argument.parent is NamedExpression) {
566 argument = argument.parent as NamedExpression;
567 }
568 ParameterElement parameter = argument.bestParameterElement;
569 if (parameter != null) {
570 DartType parameterType = parameter.type;
571 if (parameterType is FunctionType) {
572 String typeCode = _getTypeCode(parameterType.returnType);
573 if (typeCode != 'dynamic') {
574 return typeCode + ' ';
575 }
576 }
577 }
578 return '';
579 }
580
581 /**
509 * Returns the selected [Expression] source, with applying new parameter 582 * Returns the selected [Expression] source, with applying new parameter
510 * names. 583 * names.
511 */ 584 */
512 String _getMethodBodySource() { 585 String _getMethodBodySource() {
513 String source = utils.getRangeText(selectionRange); 586 String source = utils.getRangeText(selectionRange);
514 // prepare operations to replace variables with parameters 587 // prepare operations to replace variables with parameters
515 List<SourceEdit> replaceEdits = []; 588 List<SourceEdit> replaceEdits = [];
516 for (RefactoringMethodParameter parameter in _parameters) { 589 for (RefactoringMethodParameter parameter in _parameters) {
517 List<SourceRange> ranges = _parameterReferencesMap[parameter.id]; 590 List<SourceRange> ranges = _parameterReferencesMap[parameter.id];
518 if (ranges != null) { 591 if (ranges != null) {
(...skipping 731 matching lines...) Expand 10 before | Expand all | Expand 10 after
1250 return false; 1323 return false;
1251 } 1324 }
1252 for (int i = 0; i < parameterTypes.length; i++) { 1325 for (int i = 0; i < parameterTypes.length; i++) {
1253 if (other.parameterTypes[i] != parameterTypes[i]) { 1326 if (other.parameterTypes[i] != parameterTypes[i]) {
1254 return false; 1327 return false;
1255 } 1328 }
1256 } 1329 }
1257 return true; 1330 return true;
1258 } 1331 }
1259 } 1332 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/test/services/refactoring/extract_method_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698