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

Side by Side Diff: pkg/analysis_server/lib/src/edit/edit_domain.dart

Issue 2262393003: 'Extract Local' and 'Inline Local' refactoring need only single file analysis. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 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
« no previous file with comments | « no previous file | pkg/analysis_server/test/analysis_abstract.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 library edit.domain; 5 library edit.domain;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'package:analysis_server/plugin/edit/assist/assist_core.dart'; 9 import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
10 import 'package:analysis_server/plugin/edit/fix/fix_core.dart'; 10 import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
11 import 'package:analysis_server/src/analysis_server.dart'; 11 import 'package:analysis_server/src/analysis_server.dart';
12 import 'package:analysis_server/src/collections.dart'; 12 import 'package:analysis_server/src/collections.dart';
13 import 'package:analysis_server/src/constants.dart'; 13 import 'package:analysis_server/src/constants.dart';
14 import 'package:analysis_server/src/protocol_server.dart' hide Element; 14 import 'package:analysis_server/src/protocol_server.dart' hide Element;
15 import 'package:analysis_server/src/services/correction/assist.dart'; 15 import 'package:analysis_server/src/services/correction/assist.dart';
16 import 'package:analysis_server/src/services/correction/fix.dart'; 16 import 'package:analysis_server/src/services/correction/fix.dart';
17 import 'package:analysis_server/src/services/correction/organize_directives.dart '; 17 import 'package:analysis_server/src/services/correction/organize_directives.dart ';
18 import 'package:analysis_server/src/services/correction/sort_members.dart'; 18 import 'package:analysis_server/src/services/correction/sort_members.dart';
19 import 'package:analysis_server/src/services/correction/status.dart'; 19 import 'package:analysis_server/src/services/correction/status.dart';
20 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; 20 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
21 import 'package:analysis_server/src/services/search/search_engine.dart'; 21 import 'package:analysis_server/src/services/search/search_engine.dart';
22 import 'package:analyzer/dart/ast/ast.dart'; 22 import 'package:analyzer/dart/ast/ast.dart';
23 import 'package:analyzer/dart/element/element.dart'; 23 import 'package:analyzer/dart/element/element.dart';
24 import 'package:analyzer/src/dart/scanner/scanner.dart' as engine; 24 import 'package:analyzer/src/dart/scanner/scanner.dart' as engine;
25 import 'package:analyzer/src/generated/engine.dart' as engine; 25 import 'package:analyzer/src/generated/engine.dart' as engine;
26 import 'package:analyzer/src/generated/error.dart' as engine; 26 import 'package:analyzer/src/generated/error.dart' as engine;
27 import 'package:analyzer/src/generated/parser.dart' as engine; 27 import 'package:analyzer/src/generated/parser.dart' as engine;
28 import 'package:analyzer/src/generated/source.dart'; 28 import 'package:analyzer/src/generated/source.dart';
29 import 'package:analyzer/task/dart.dart';
29 import 'package:dart_style/dart_style.dart'; 30 import 'package:dart_style/dart_style.dart';
30 31
32 int test_resetCount = 0;
33
31 bool test_simulateRefactoringException_change = false; 34 bool test_simulateRefactoringException_change = false;
32 bool test_simulateRefactoringException_final = false; 35 bool test_simulateRefactoringException_final = false;
33 bool test_simulateRefactoringException_init = false; 36 bool test_simulateRefactoringException_init = false;
34 37
35 bool test_simulateRefactoringReset_afterCreateChange = false; 38 bool test_simulateRefactoringReset_afterCreateChange = false;
36 bool test_simulateRefactoringReset_afterFinalConditions = false; 39 bool test_simulateRefactoringReset_afterFinalConditions = false;
37 bool test_simulateRefactoringReset_afterInitialConditions = false; 40 bool test_simulateRefactoringReset_afterInitialConditions = false;
38 41
39 /** 42 /**
40 * Instances of the class [EditDomainHandler] implement a [RequestHandler] 43 * Instances of the class [EditDomainHandler] implement a [RequestHandler]
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after
369 * 372 *
370 * Once new set of parameters is received, the previous [Refactoring] instance 373 * Once new set of parameters is received, the previous [Refactoring] instance
371 * is invalidated and a new one is created and initialized. 374 * is invalidated and a new one is created and initialized.
372 */ 375 */
373 class _RefactoringManager { 376 class _RefactoringManager {
374 static const List<RefactoringProblem> EMPTY_PROBLEM_LIST = 377 static const List<RefactoringProblem> EMPTY_PROBLEM_LIST =
375 const <RefactoringProblem>[]; 378 const <RefactoringProblem>[];
376 379
377 final AnalysisServer server; 380 final AnalysisServer server;
378 final SearchEngine searchEngine; 381 final SearchEngine searchEngine;
379 StreamSubscription onAnalysisStartedSubscription; 382 StreamSubscription subscriptionToReset;
380 383
381 RefactoringKind kind; 384 RefactoringKind kind;
382 String file; 385 String file;
383 int offset; 386 int offset;
384 int length; 387 int length;
385 Refactoring refactoring; 388 Refactoring refactoring;
386 RefactoringFeedback feedback; 389 RefactoringFeedback feedback;
387 RefactoringStatus initStatus; 390 RefactoringStatus initStatus;
388 RefactoringStatus optionsStatus; 391 RefactoringStatus optionsStatus;
389 RefactoringStatus finalStatus; 392 RefactoringStatus finalStatus;
390 393
391 Request request; 394 Request request;
392 EditGetRefactoringResult result; 395 EditGetRefactoringResult result;
393 396
394 _RefactoringManager(this.server, this.searchEngine) { 397 _RefactoringManager(this.server, this.searchEngine) {
395 onAnalysisStartedSubscription = server.onAnalysisStarted.listen(_reset);
396 _reset(); 398 _reset();
397 } 399 }
398 400
399 /** 401 /**
400 * Returns `true` if a response for the current request has not yet been sent. 402 * Returns `true` if a response for the current request has not yet been sent.
401 */ 403 */
402 bool get hasPendingRequest => request != null; 404 bool get hasPendingRequest => request != null;
403 405
404 bool get _hasFatalError { 406 bool get _hasFatalError {
405 return initStatus.hasFatalError || 407 return initStatus.hasFatalError ||
406 optionsStatus.hasFatalError || 408 optionsStatus.hasFatalError ||
407 finalStatus.hasFatalError; 409 finalStatus.hasFatalError;
408 } 410 }
409 411
410 /** 412 /**
411 * Checks if [refactoring] requires options. 413 * Checks if [refactoring] requires options.
412 */ 414 */
413 bool get _requiresOptions { 415 bool get _requiresOptions {
414 return refactoring is ExtractLocalRefactoring || 416 return refactoring is ExtractLocalRefactoring ||
415 refactoring is ExtractMethodRefactoring || 417 refactoring is ExtractMethodRefactoring ||
416 refactoring is InlineMethodRefactoring || 418 refactoring is InlineMethodRefactoring ||
417 refactoring is MoveFileRefactoring || 419 refactoring is MoveFileRefactoring ||
418 refactoring is RenameRefactoring; 420 refactoring is RenameRefactoring;
419 } 421 }
420 422
421 /** 423 /**
422 * Cancels processing of the current request and cleans up. 424 * Cancels processing of the current request and cleans up.
423 */ 425 */
424 void cancel() { 426 void cancel() {
425 onAnalysisStartedSubscription.cancel(); 427 subscriptionToReset?.cancel();
428 subscriptionToReset = null;
426 server.sendResponse(new Response.refactoringRequestCancelled(request)); 429 server.sendResponse(new Response.refactoringRequestCancelled(request));
427 request = null; 430 request = null;
428 } 431 }
429 432
430 void getRefactoring(Request _request) { 433 void getRefactoring(Request _request) {
431 // prepare for processing the request 434 // prepare for processing the request
432 request = _request; 435 request = _request;
433 result = new EditGetRefactoringResult( 436 result = new EditGetRefactoringResult(
434 EMPTY_PROBLEM_LIST, EMPTY_PROBLEM_LIST, EMPTY_PROBLEM_LIST); 437 EMPTY_PROBLEM_LIST, EMPTY_PROBLEM_LIST, EMPTY_PROBLEM_LIST);
435 // process the request 438 // process the request
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
485 cancel(); 488 cancel();
486 } else { 489 } else {
487 server.instrumentationService.logException(exception, stackTrace); 490 server.instrumentationService.logException(exception, stackTrace);
488 server.sendResponse( 491 server.sendResponse(
489 new Response.serverError(_request, exception, stackTrace)); 492 new Response.serverError(_request, exception, stackTrace));
490 } 493 }
491 _reset(); 494 _reset();
492 }); 495 });
493 } 496 }
494 497
498 /**
499 * Perform enough analysis to be able to perform refactoring of the given
500 * [kind] in the given [file].
501 */
502 Future<Null> _analyzeForRefactoring(String file, RefactoringKind kind) async {
503 // "Extract Local" and "Inline Local" refactorings need only local analysis.
504 if (kind == RefactoringKind.EXTRACT_LOCAL_VARIABLE ||
505 kind == RefactoringKind.INLINE_LOCAL_VARIABLE) {
506 ContextSourcePair pair = server.getContextSourcePair(file);
507 engine.AnalysisContext context = pair.context;
508 Source source = pair.source;
509 if (context != null && source != null) {
510 if (context.computeResult(source, SOURCE_KIND) == SourceKind.LIBRARY) {
511 await context.computeResolvedCompilationUnitAsync(source, source);
512 return;
513 }
514 }
515 }
516 // A refactoring for which we cannot optimize analysis.
517 // So, wait for full analysis.
518 await server.onAnalysisComplete;
519 }
520
495 void _checkForReset_afterCreateChange() { 521 void _checkForReset_afterCreateChange() {
496 if (test_simulateRefactoringReset_afterCreateChange) { 522 if (test_simulateRefactoringReset_afterCreateChange) {
497 _reset(); 523 _reset();
498 } 524 }
499 if (refactoring == null) { 525 if (refactoring == null) {
500 throw new _ResetError(); 526 throw new _ResetError();
501 } 527 }
502 } 528 }
503 529
504 void _checkForReset_afterFinalConditions() { 530 void _checkForReset_afterFinalConditions() {
(...skipping 13 matching lines...) Expand all
518 throw new _ResetError(); 544 throw new _ResetError();
519 } 545 }
520 } 546 }
521 547
522 /** 548 /**
523 * Initializes this context to perform a refactoring with the specified 549 * Initializes this context to perform a refactoring with the specified
524 * parameters. The existing [Refactoring] is reused or created as needed. 550 * parameters. The existing [Refactoring] is reused or created as needed.
525 */ 551 */
526 Future _init( 552 Future _init(
527 RefactoringKind kind, String file, int offset, int length) async { 553 RefactoringKind kind, String file, int offset, int length) async {
528 await server.onAnalysisComplete; 554 await _analyzeForRefactoring(file, kind);
529 // check if we can continue with the existing Refactoring instance 555 // check if we can continue with the existing Refactoring instance
530 if (this.kind == kind && 556 if (this.kind == kind &&
531 this.file == file && 557 this.file == file &&
532 this.offset == offset && 558 this.offset == offset &&
533 this.length == length) { 559 this.length == length) {
534 return; 560 return;
535 } 561 }
536 _reset(); 562 _reset();
537 this.kind = kind; 563 this.kind = kind;
538 this.file = file; 564 this.file = file;
539 this.offset = offset; 565 this.offset = offset;
540 this.length = length; 566 this.length = length;
541 // simulate an exception 567 // simulate an exception
542 if (test_simulateRefactoringException_init) { 568 if (test_simulateRefactoringException_init) {
543 throw 'A simulated refactoring exception - init.'; 569 throw 'A simulated refactoring exception - init.';
544 } 570 }
545 // create a new Refactoring instance 571 // create a new Refactoring instance
546 if (kind == RefactoringKind.CONVERT_GETTER_TO_METHOD) { 572 if (kind == RefactoringKind.CONVERT_GETTER_TO_METHOD) {
547 List<Element> elements = server.getElementsAtOffset(file, offset); 573 List<Element> elements = server.getElementsAtOffset(file, offset);
548 if (elements.isNotEmpty) { 574 if (elements.isNotEmpty) {
549 Element element = elements[0]; 575 Element element = elements[0];
550 if (element is ExecutableElement) { 576 if (element is ExecutableElement) {
577 _resetOnAnalysisStarted();
551 refactoring = 578 refactoring =
552 new ConvertGetterToMethodRefactoring(searchEngine, element); 579 new ConvertGetterToMethodRefactoring(searchEngine, element);
553 } 580 }
554 } 581 }
555 } 582 }
556 if (kind == RefactoringKind.CONVERT_METHOD_TO_GETTER) { 583 if (kind == RefactoringKind.CONVERT_METHOD_TO_GETTER) {
557 List<Element> elements = server.getElementsAtOffset(file, offset); 584 List<Element> elements = server.getElementsAtOffset(file, offset);
558 if (elements.isNotEmpty) { 585 if (elements.isNotEmpty) {
559 Element element = elements[0]; 586 Element element = elements[0];
560 if (element is ExecutableElement) { 587 if (element is ExecutableElement) {
588 _resetOnAnalysisStarted();
561 refactoring = 589 refactoring =
562 new ConvertMethodToGetterRefactoring(searchEngine, element); 590 new ConvertMethodToGetterRefactoring(searchEngine, element);
563 } 591 }
564 } 592 }
565 } 593 }
566 if (kind == RefactoringKind.EXTRACT_LOCAL_VARIABLE) { 594 if (kind == RefactoringKind.EXTRACT_LOCAL_VARIABLE) {
567 List<CompilationUnit> units = server.getResolvedCompilationUnits(file); 595 List<CompilationUnit> units = server.getResolvedCompilationUnits(file);
568 if (units.isNotEmpty) { 596 if (units.isNotEmpty) {
597 _resetOnFileResolutionChanged(file);
569 refactoring = new ExtractLocalRefactoring(units[0], offset, length); 598 refactoring = new ExtractLocalRefactoring(units[0], offset, length);
570 feedback = new ExtractLocalVariableFeedback( 599 feedback = new ExtractLocalVariableFeedback(
571 <String>[], <int>[], <int>[], 600 <String>[], <int>[], <int>[],
572 coveringExpressionOffsets: <int>[], 601 coveringExpressionOffsets: <int>[],
573 coveringExpressionLengths: <int>[]); 602 coveringExpressionLengths: <int>[]);
574 } 603 }
575 } 604 }
576 if (kind == RefactoringKind.EXTRACT_METHOD) { 605 if (kind == RefactoringKind.EXTRACT_METHOD) {
577 List<CompilationUnit> units = server.getResolvedCompilationUnits(file); 606 List<CompilationUnit> units = server.getResolvedCompilationUnits(file);
578 if (units.isNotEmpty) { 607 if (units.isNotEmpty) {
608 _resetOnAnalysisStarted();
579 refactoring = new ExtractMethodRefactoring( 609 refactoring = new ExtractMethodRefactoring(
580 searchEngine, units[0], offset, length); 610 searchEngine, units[0], offset, length);
581 feedback = new ExtractMethodFeedback(offset, length, '', <String>[], 611 feedback = new ExtractMethodFeedback(offset, length, '', <String>[],
582 false, <RefactoringMethodParameter>[], <int>[], <int>[]); 612 false, <RefactoringMethodParameter>[], <int>[], <int>[]);
583 } 613 }
584 } 614 }
585 if (kind == RefactoringKind.INLINE_LOCAL_VARIABLE) { 615 if (kind == RefactoringKind.INLINE_LOCAL_VARIABLE) {
586 List<CompilationUnit> units = server.getResolvedCompilationUnits(file); 616 List<CompilationUnit> units = server.getResolvedCompilationUnits(file);
587 if (units.isNotEmpty) { 617 if (units.isNotEmpty) {
618 _resetOnFileResolutionChanged(file);
588 refactoring = 619 refactoring =
589 new InlineLocalRefactoring(searchEngine, units[0], offset); 620 new InlineLocalRefactoring(searchEngine, units[0], offset);
590 } 621 }
591 } 622 }
592 if (kind == RefactoringKind.INLINE_METHOD) { 623 if (kind == RefactoringKind.INLINE_METHOD) {
593 List<CompilationUnit> units = server.getResolvedCompilationUnits(file); 624 List<CompilationUnit> units = server.getResolvedCompilationUnits(file);
594 if (units.isNotEmpty) { 625 if (units.isNotEmpty) {
626 _resetOnAnalysisStarted();
595 refactoring = 627 refactoring =
596 new InlineMethodRefactoring(searchEngine, units[0], offset); 628 new InlineMethodRefactoring(searchEngine, units[0], offset);
597 } 629 }
598 } 630 }
599 if (kind == RefactoringKind.MOVE_FILE) { 631 if (kind == RefactoringKind.MOVE_FILE) {
632 _resetOnAnalysisStarted();
600 ContextSourcePair contextSource = server.getContextSourcePair(file); 633 ContextSourcePair contextSource = server.getContextSourcePair(file);
601 engine.AnalysisContext context = contextSource.context; 634 engine.AnalysisContext context = contextSource.context;
602 Source source = contextSource.source; 635 Source source = contextSource.source;
603 refactoring = new MoveFileRefactoring( 636 refactoring = new MoveFileRefactoring(
604 server.resourceProvider, searchEngine, context, source, file); 637 server.resourceProvider, searchEngine, context, source, file);
605 } 638 }
606 if (kind == RefactoringKind.RENAME) { 639 if (kind == RefactoringKind.RENAME) {
607 List<AstNode> nodes = server.getNodesAtOffset(file, offset); 640 List<AstNode> nodes = server.getNodesAtOffset(file, offset);
608 List<Element> elements = server.getElementsOfNodes(nodes); 641 List<Element> elements = server.getElementsOfNodes(nodes);
609 if (nodes.isNotEmpty && elements.isNotEmpty) { 642 if (nodes.isNotEmpty && elements.isNotEmpty) {
610 AstNode node = nodes[0]; 643 AstNode node = nodes[0];
611 Element element = elements[0]; 644 Element element = elements[0];
612 if (element is FieldFormalParameterElement) { 645 if (element is FieldFormalParameterElement) {
613 element = (element as FieldFormalParameterElement).field; 646 element = (element as FieldFormalParameterElement).field;
614 } 647 }
615 // climb from "Class" in "new Class.named()" to "Class.named" 648 // climb from "Class" in "new Class.named()" to "Class.named"
616 if (node.parent is TypeName && node.parent.parent is ConstructorName) { 649 if (node.parent is TypeName && node.parent.parent is ConstructorName) {
617 ConstructorName constructor = node.parent.parent; 650 ConstructorName constructor = node.parent.parent;
618 node = constructor; 651 node = constructor;
619 element = constructor.staticElement; 652 element = constructor.staticElement;
620 } 653 }
621 // do create the refactoring 654 // do create the refactoring
655 _resetOnAnalysisStarted();
622 refactoring = new RenameRefactoring(searchEngine, element); 656 refactoring = new RenameRefactoring(searchEngine, element);
623 feedback = 657 feedback =
624 new RenameFeedback(node.offset, node.length, 'kind', 'oldName'); 658 new RenameFeedback(node.offset, node.length, 'kind', 'oldName');
625 } 659 }
626 } 660 }
627 if (refactoring == null) { 661 if (refactoring == null) {
628 initStatus = 662 initStatus =
629 new RefactoringStatus.fatal('Unable to create a refactoring'); 663 new RefactoringStatus.fatal('Unable to create a refactoring');
630 return; 664 return;
631 } 665 }
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
669 } 703 }
670 } 704 }
671 if (refactoring is RenameRefactoring) { 705 if (refactoring is RenameRefactoring) {
672 RenameRefactoring refactoring = this.refactoring; 706 RenameRefactoring refactoring = this.refactoring;
673 RenameFeedback feedback = this.feedback; 707 RenameFeedback feedback = this.feedback;
674 feedback.elementKindName = refactoring.elementKindName; 708 feedback.elementKindName = refactoring.elementKindName;
675 feedback.oldName = refactoring.oldName; 709 feedback.oldName = refactoring.oldName;
676 } 710 }
677 } 711 }
678 712
679 void _reset([engine.AnalysisContext context]) { 713 void _reset() {
714 test_resetCount++;
680 kind = null; 715 kind = null;
681 offset = null; 716 offset = null;
682 length = null; 717 length = null;
683 refactoring = null; 718 refactoring = null;
684 feedback = null; 719 feedback = null;
685 initStatus = new RefactoringStatus(); 720 initStatus = new RefactoringStatus();
686 optionsStatus = new RefactoringStatus(); 721 optionsStatus = new RefactoringStatus();
687 finalStatus = new RefactoringStatus(); 722 finalStatus = new RefactoringStatus();
723 subscriptionToReset?.cancel();
724 subscriptionToReset = null;
725 }
726
727 void _resetOnAnalysisStarted() {
728 subscriptionToReset = server.onAnalysisStarted.listen((_) => _reset());
Brian Wilkerson 2016/08/22 23:32:23 Should we assert that `subscriptionToReset` is nul
scheglov 2016/08/23 01:55:28 Well, we need to at least cancel the current subsc
729 }
730
731 /**
732 * We're performing a refactoring that affects only the given [file].
733 * So, when the [file] resolution is changed, we need to reset refactoring.
734 * But when any other file is changed or analyzer, we can continue.
735 */
736 void _resetOnFileResolutionChanged(String file) {
737 engine.AnalysisContext context = server.getAnalysisContext(file);
738 subscriptionToReset =
739 context?.onResultChanged(RESOLVED_UNIT)?.listen((event) {
740 Source targetSource = event.target.source;
741 if (targetSource?.fullName == file) {
742 _reset();
743 }
744 });
688 } 745 }
689 746
690 void _sendResultResponse() { 747 void _sendResultResponse() {
691 // ignore if was cancelled 748 // ignore if was cancelled
692 if (request == null) { 749 if (request == null) {
693 return; 750 return;
694 } 751 }
695 // set feedback 752 // set feedback
696 result.feedback = feedback; 753 result.feedback = feedback;
697 // set problems 754 // set problems
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
746 } 803 }
747 return new RefactoringStatus(); 804 return new RefactoringStatus();
748 } 805 }
749 } 806 }
750 807
751 /** 808 /**
752 * [_RefactoringManager] throws instances of this class internally to stop 809 * [_RefactoringManager] throws instances of this class internally to stop
753 * processing in a manager that was reset. 810 * processing in a manager that was reset.
754 */ 811 */
755 class _ResetError {} 812 class _ResetError {}
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/test/analysis_abstract.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698