Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 analysis.server; | 5 library analysis.server; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 import 'dart:math' show max; | 9 import 'dart:math' show max; |
| 10 | 10 |
| (...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 360 } | 360 } |
| 361 | 361 |
| 362 /** | 362 /** |
| 363 * Return the preferred [AnalysisContext] for analyzing the given [path]. | 363 * Return the preferred [AnalysisContext] for analyzing the given [path]. |
| 364 * This will be the context that explicitly contains the path, if any such | 364 * This will be the context that explicitly contains the path, if any such |
| 365 * context exists, otherwise it will be the first analysis context that | 365 * context exists, otherwise it will be the first analysis context that |
| 366 * implicitly analyzes it. Return `null` if no context is analyzing the | 366 * implicitly analyzes it. Return `null` if no context is analyzing the |
| 367 * path. | 367 * path. |
| 368 */ | 368 */ |
| 369 AnalysisContext getAnalysisContext(String path) { | 369 AnalysisContext getAnalysisContext(String path) { |
| 370 // try to find a containing context | 370 return getContextSourcePair(path).context; |
| 371 Folder containingFolder = null; | |
| 372 for (Folder folder in folderMap.keys) { | |
| 373 if (folder.path == path || folder.contains(path)) { | |
| 374 if (containingFolder == null) { | |
| 375 containingFolder = folder; | |
| 376 } else if (containingFolder.path.length < folder.path.length) { | |
| 377 containingFolder = folder; | |
| 378 } | |
| 379 } | |
| 380 } | |
| 381 if (containingFolder != null) { | |
| 382 return folderMap[containingFolder]; | |
| 383 } | |
| 384 Resource resource = resourceProvider.getResource(path); | |
| 385 if (resource is Folder) { | |
| 386 return null; | |
| 387 } | |
| 388 // check if there is a context that analyzed this source | |
| 389 return getAnalysisContextForSource(_getSourceWithoutContext(path)); | |
| 390 } | 371 } |
| 391 | 372 |
| 392 /** | 373 /** |
| 374 * Return the [AnalysisContext] that contains the given [path]. | |
| 375 * Return `null` if no context contains the [path]. | |
| 376 */ | |
| 377 AnalysisContext getContainingContext(String path) { | |
| 378 Folder containingFolder = null; | |
| 379 AnalysisContext containingContext = null; | |
| 380 folderMap.forEach((Folder folder, AnalysisContext context) { | |
| 381 if (folder.isOrContains(path)) { | |
| 382 if (containingFolder == null || | |
| 383 containingFolder.path.length < folder.path.length) { | |
| 384 containingFolder = folder; | |
| 385 containingContext = context; | |
| 386 } | |
| 387 } | |
| 388 }); | |
| 389 return containingContext; | |
| 390 } | |
| 391 | |
| 392 /** | |
| 393 * Return any [AnalysisContext] that is analyzing the given [source], either | 393 * Return any [AnalysisContext] that is analyzing the given [source], either |
| 394 * explicitly or implicitly. Return `null` if there is no such context. | 394 * explicitly or implicitly. Return `null` if there is no such context. |
| 395 */ | 395 */ |
| 396 AnalysisContext getAnalysisContextForSource(Source source) { | 396 AnalysisContext getAnalysisContextForSource(Source source) { |
| 397 for (AnalysisContext context in folderMap.values) { | 397 for (AnalysisContext context in folderMap.values) { |
| 398 SourceKind kind = context.getKindOf(source); | 398 SourceKind kind = context.getKindOf(source); |
| 399 if (kind != SourceKind.UNKNOWN) { | 399 if (kind != SourceKind.UNKNOWN) { |
| 400 return context; | 400 return context; |
| 401 } | 401 } |
| 402 } | 402 } |
| 403 return null; | 403 return null; |
| 404 } | 404 } |
| 405 | 405 |
| 406 /** | 406 /** |
| 407 * Return the [AnalysisContext]s that are being used to analyze the analysis | 407 * Return the [AnalysisContext]s that are being used to analyze the analysis |
| 408 * roots. | 408 * roots. |
| 409 */ | 409 */ |
| 410 Iterable<AnalysisContext> getAnalysisContexts() { | 410 Iterable<AnalysisContext> getAnalysisContexts() { |
| 411 return folderMap.values; | 411 return folderMap.values; |
| 412 } | 412 } |
| 413 | 413 |
| 414 /** | 414 /** |
| 415 * Return the primary [ContextSourcePair] representing the given [path]. | |
| 416 * | |
| 417 * The [AnalysisContext] of this pair will be the context that explicitly | |
| 418 * contains the path, if any such context exists, otherwise it will be the | |
| 419 * first context that implicitly analyzes it. | |
| 420 * | |
| 421 * If the [path] is not analyzed by any context, a [ContextSourcePair] with | |
| 422 * `null` context and `file` [Source] is returned. | |
| 423 * | |
| 424 * If the [path] dosn't represent a file, `null` is returned as a [Source]. | |
| 425 * | |
| 426 * Does not return `null`. | |
| 427 */ | |
| 428 ContextSourcePair getContextSourcePair(String path) { | |
| 429 // try SDK | |
| 430 { | |
| 431 Uri uri = resourceProvider.pathContext.toUri(path); | |
| 432 Source sdkSource = defaultSdk.fromFileUri(uri); | |
| 433 if (sdkSource != null) { | |
| 434 AnalysisContext anyContext = folderMap.values.first; | |
| 435 return new ContextSourcePair(anyContext, sdkSource); | |
| 436 } | |
| 437 } | |
| 438 // try to find the deep-most containing context | |
| 439 Resource resource = resourceProvider.getResource(path); | |
| 440 File file = resource is File ? resource : null; | |
| 441 { | |
| 442 Folder containingFolder = null; | |
|
Brian Wilkerson
2015/03/06 15:04:27
This is very similar to getContainingContext; it w
scheglov
2015/03/06 21:47:03
Done.
| |
| 443 AnalysisContext containingContext = null; | |
| 444 folderMap.forEach((Folder folder, AnalysisContext context) { | |
| 445 if (folder.isOrContains(path)) { | |
| 446 if (containingFolder == null || | |
| 447 containingFolder.path.length < folder.path.length) { | |
| 448 containingFolder = folder; | |
| 449 containingContext = context; | |
| 450 } | |
| 451 } | |
| 452 }); | |
| 453 if (containingContext != null) { | |
| 454 Source source = file != null | |
| 455 ? ContextManager.createSourceInContext(containingContext, file) | |
| 456 : null; | |
| 457 return new ContextSourcePair(containingContext, source); | |
| 458 } | |
| 459 } | |
| 460 // try to find a context that analysed the file | |
| 461 for (AnalysisContext context in folderMap.values) { | |
| 462 Source source = file != null | |
| 463 ? ContextManager.createSourceInContext(context, file) | |
| 464 : null; | |
| 465 SourceKind kind = context.getKindOf(source); | |
| 466 if (kind != SourceKind.UNKNOWN) { | |
| 467 return new ContextSourcePair(context, source); | |
| 468 } | |
| 469 } | |
| 470 // file-based source | |
| 471 Source fileSource = file != null ? file.createSource() : null; | |
| 472 return new ContextSourcePair(null, fileSource); | |
| 473 } | |
| 474 | |
| 475 /** | |
| 415 * Returns [Element]s at the given [offset] of the given [file]. | 476 * Returns [Element]s at the given [offset] of the given [file]. |
| 416 * | 477 * |
| 417 * May be empty if cannot be resolved, but not `null`. | 478 * May be empty if cannot be resolved, but not `null`. |
| 418 */ | 479 */ |
| 419 List<Element> getElementsAtOffset(String file, int offset) { | 480 List<Element> getElementsAtOffset(String file, int offset) { |
| 420 List<AstNode> nodes = getNodesAtOffset(file, offset); | 481 List<AstNode> nodes = getNodesAtOffset(file, offset); |
| 421 return getElementsOfNodes(nodes, offset); | 482 return getElementsOfNodes(nodes, offset); |
| 422 } | 483 } |
| 423 | 484 |
| 424 /** | 485 /** |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 453 * Returns `null` if [file] does not belong to any [AnalysisContext], or the | 514 * Returns `null` if [file] does not belong to any [AnalysisContext], or the |
| 454 * file does not exist. | 515 * file does not exist. |
| 455 * | 516 * |
| 456 * The array of errors will be empty if there are no errors in [file]. The | 517 * The array of errors will be empty if there are no errors in [file]. The |
| 457 * errors contained in the array can be incomplete. | 518 * errors contained in the array can be incomplete. |
| 458 * | 519 * |
| 459 * This method does not wait for all errors to be computed, and returns just | 520 * This method does not wait for all errors to be computed, and returns just |
| 460 * the current state. | 521 * the current state. |
| 461 */ | 522 */ |
| 462 AnalysisErrorInfo getErrors(String file) { | 523 AnalysisErrorInfo getErrors(String file) { |
| 463 // prepare AnalysisContext | 524 ContextSourcePair contextSource = getContextSourcePair(file); |
| 464 AnalysisContext context = getAnalysisContext(file); | 525 AnalysisContext context = contextSource.context; |
| 526 Source source = contextSource.source; | |
| 465 if (context == null) { | 527 if (context == null) { |
| 466 return null; | 528 return null; |
| 467 } | 529 } |
| 468 // prepare Source | 530 if (!source.exists()) { |
| 469 Source source = getSource(file); | |
| 470 if (context.getKindOf(source) == SourceKind.UNKNOWN) { | |
| 471 return null; | 531 return null; |
| 472 } | 532 } |
| 473 // get errors for the file | |
| 474 return context.getErrors(source); | 533 return context.getErrors(source); |
| 475 } | 534 } |
| 476 | 535 |
| 477 /** | |
| 478 * Returns resolved [AstNode]s at the given [offset] of the given [file]. | |
| 479 * | |
| 480 * May be empty, but not `null`. | |
| 481 */ | |
| 482 List<AstNode> getNodesAtOffset(String file, int offset) { | |
| 483 List<CompilationUnit> units = getResolvedCompilationUnits(file); | |
| 484 List<AstNode> nodes = <AstNode>[]; | |
| 485 for (CompilationUnit unit in units) { | |
| 486 AstNode node = new NodeLocator.con1(offset).searchWithin(unit); | |
| 487 if (node != null) { | |
| 488 nodes.add(node); | |
| 489 } | |
| 490 } | |
| 491 return nodes; | |
| 492 } | |
| 493 | |
| 494 // TODO(brianwilkerson) Add the following method after 'prioritySources' has | 536 // TODO(brianwilkerson) Add the following method after 'prioritySources' has |
| 495 // been added to InternalAnalysisContext. | 537 // been added to InternalAnalysisContext. |
| 496 // /** | 538 // /** |
| 497 // * Return a list containing the full names of all of the sources that are | 539 // * Return a list containing the full names of all of the sources that are |
| 498 // * priority sources. | 540 // * priority sources. |
| 499 // */ | 541 // */ |
| 500 // List<String> getPriorityFiles() { | 542 // List<String> getPriorityFiles() { |
| 501 // List<String> priorityFiles = new List<String>(); | 543 // List<String> priorityFiles = new List<String>(); |
| 502 // folderMap.values.forEach((ContextDirectory directory) { | 544 // folderMap.values.forEach((ContextDirectory directory) { |
| 503 // InternalAnalysisContext context = directory.context; | 545 // InternalAnalysisContext context = directory.context; |
| 504 // context.prioritySources.forEach((Source source) { | 546 // context.prioritySources.forEach((Source source) { |
| 505 // priorityFiles.add(source.fullName); | 547 // priorityFiles.add(source.fullName); |
| 506 // }); | 548 // }); |
| 507 // }); | 549 // }); |
| 508 // return priorityFiles; | 550 // return priorityFiles; |
| 509 // } | 551 // } |
| 510 | 552 |
| 511 /** | 553 /** |
| 554 * Returns resolved [AstNode]s at the given [offset] of the given [file]. | |
| 555 * | |
| 556 * May be empty, but not `null`. | |
| 557 */ | |
| 558 List<AstNode> getNodesAtOffset(String file, int offset) { | |
| 559 List<CompilationUnit> units = getResolvedCompilationUnits(file); | |
| 560 List<AstNode> nodes = <AstNode>[]; | |
| 561 for (CompilationUnit unit in units) { | |
| 562 AstNode node = new NodeLocator.con1(offset).searchWithin(unit); | |
| 563 if (node != null) { | |
| 564 nodes.add(node); | |
| 565 } | |
| 566 } | |
| 567 return nodes; | |
| 568 } | |
| 569 | |
| 570 /** | |
| 512 * Returns resolved [CompilationUnit]s of the Dart file with the given [path]. | 571 * Returns resolved [CompilationUnit]s of the Dart file with the given [path]. |
| 513 * | 572 * |
| 514 * May be empty, but not `null`. | 573 * May be empty, but not `null`. |
| 515 */ | 574 */ |
| 516 List<CompilationUnit> getResolvedCompilationUnits(String path) { | 575 List<CompilationUnit> getResolvedCompilationUnits(String path) { |
| 517 List<CompilationUnit> units = <CompilationUnit>[]; | 576 List<CompilationUnit> units = <CompilationUnit>[]; |
| 577 ContextSourcePair contextSource = getContextSourcePair(path); | |
| 518 // prepare AnalysisContext | 578 // prepare AnalysisContext |
| 519 AnalysisContext context = getAnalysisContext(path); | 579 AnalysisContext context = contextSource.context; |
| 520 if (context == null) { | 580 if (context == null) { |
| 521 return units; | 581 return units; |
| 522 } | 582 } |
| 523 // add a unit for each unit/library combination | 583 // add a unit for each unit/library combination |
| 524 Source unitSource = getSource(path); | 584 Source unitSource = contextSource.source; |
| 525 List<Source> librarySources = context.getLibrariesContaining(unitSource); | 585 List<Source> librarySources = context.getLibrariesContaining(unitSource); |
| 526 for (Source librarySource in librarySources) { | 586 for (Source librarySource in librarySources) { |
| 527 CompilationUnit unit = | 587 CompilationUnit unit = |
| 528 context.resolveCompilationUnit2(unitSource, librarySource); | 588 context.resolveCompilationUnit2(unitSource, librarySource); |
| 529 if (unit != null) { | 589 if (unit != null) { |
| 530 units.add(unit); | 590 units.add(unit); |
| 531 } | 591 } |
| 532 } | 592 } |
| 533 // done | 593 // done |
| 534 return units; | 594 return units; |
| 535 } | 595 } |
| 536 | 596 |
| 537 /** | 597 /** |
| 538 * Returns the [CompilationUnit] of the Dart file with the given [path] that | |
| 539 * should be used to resend notifications for already resolved unit. | |
| 540 * Returns `null` if the file is not a part of any context, library has not | |
| 541 * been yet resolved, or any problem happened. | |
| 542 */ | |
| 543 CompilationUnit getResolvedCompilationUnitToResendNotification(String path) { | |
| 544 // prepare AnalysisContext | |
| 545 AnalysisContext context = getAnalysisContext(path); | |
| 546 if (context == null) { | |
| 547 return null; | |
| 548 } | |
| 549 // prepare sources | |
| 550 Source unitSource = getSource(path); | |
| 551 List<Source> librarySources = context.getLibrariesContaining(unitSource); | |
| 552 if (librarySources.isEmpty) { | |
| 553 return null; | |
| 554 } | |
| 555 // if library has not been resolved yet, the unit will be resolved later | |
| 556 Source librarySource = librarySources[0]; | |
| 557 if (context.getLibraryElement(librarySource) == null) { | |
| 558 return null; | |
| 559 } | |
| 560 // if library has been already resolved, resolve unit | |
| 561 return context.resolveCompilationUnit2(unitSource, librarySource); | |
| 562 } | |
| 563 | |
| 564 /** | |
| 565 * Return the [Source] of the Dart file with the given [path]. | |
| 566 */ | |
| 567 Source getSource(String path) { | |
| 568 // try SDK | |
| 569 { | |
| 570 Uri uri = resourceProvider.pathContext.toUri(path); | |
| 571 Source sdkSource = defaultSdk.fromFileUri(uri); | |
| 572 if (sdkSource != null) { | |
| 573 return sdkSource; | |
| 574 } | |
| 575 } | |
| 576 // file-based source | |
| 577 File file = resourceProvider.getResource(path); | |
| 578 return ContextManager.createSourceInContext(getAnalysisContext(path), file); | |
| 579 } | |
| 580 | |
| 581 /** | |
| 582 * Handle a [request] that was read from the communication channel. | 598 * Handle a [request] that was read from the communication channel. |
| 583 */ | 599 */ |
| 584 void handleRequest(Request request) { | 600 void handleRequest(Request request) { |
| 585 _performance.logRequest(request); | 601 _performance.logRequest(request); |
| 586 runZoned(() { | 602 runZoned(() { |
| 587 ServerPerformanceStatistics.serverRequests.makeCurrentWhile(() { | 603 ServerPerformanceStatistics.serverRequests.makeCurrentWhile(() { |
| 588 int count = handlers.length; | 604 int count = handlers.length; |
| 589 for (int i = 0; i < count; i++) { | 605 for (int i = 0; i < count; i++) { |
| 590 try { | 606 try { |
| 591 Response response = handlers[i].handleRequest(request); | 607 Response response = handlers[i].handleRequest(request); |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 845 * Implementation for `analysis.setSubscriptions`. | 861 * Implementation for `analysis.setSubscriptions`. |
| 846 */ | 862 */ |
| 847 void setAnalysisSubscriptions( | 863 void setAnalysisSubscriptions( |
| 848 Map<AnalysisService, Set<String>> subscriptions) { | 864 Map<AnalysisService, Set<String>> subscriptions) { |
| 849 // send notifications for already analyzed sources | 865 // send notifications for already analyzed sources |
| 850 subscriptions.forEach((service, Set<String> newFiles) { | 866 subscriptions.forEach((service, Set<String> newFiles) { |
| 851 Set<String> oldFiles = analysisServices[service]; | 867 Set<String> oldFiles = analysisServices[service]; |
| 852 Set<String> todoFiles = | 868 Set<String> todoFiles = |
| 853 oldFiles != null ? newFiles.difference(oldFiles) : newFiles; | 869 oldFiles != null ? newFiles.difference(oldFiles) : newFiles; |
| 854 for (String file in todoFiles) { | 870 for (String file in todoFiles) { |
| 855 Source source = getSource(file); | 871 ContextSourcePair contextSource = getContextSourcePair(file); |
| 856 // prepare context | 872 // prepare context |
| 857 AnalysisContext context = getAnalysisContext(file); | 873 AnalysisContext context = contextSource.context; |
| 858 if (context == null) { | 874 if (context == null) { |
| 859 continue; | 875 continue; |
| 860 } | 876 } |
| 861 // Dart unit notifications. | 877 // Dart unit notifications. |
| 862 if (AnalysisEngine.isDartFileName(file)) { | 878 if (AnalysisEngine.isDartFileName(file)) { |
| 879 Source source = contextSource.source; | |
| 863 CompilationUnit dartUnit = | 880 CompilationUnit dartUnit = |
| 864 getResolvedCompilationUnitToResendNotification(file); | 881 _getResolvedCompilationUnitToResendNotification(context, source); |
| 865 if (dartUnit != null) { | 882 if (dartUnit != null) { |
| 866 switch (service) { | 883 switch (service) { |
| 867 case AnalysisService.HIGHLIGHTS: | 884 case AnalysisService.HIGHLIGHTS: |
| 868 sendAnalysisNotificationHighlights(this, file, dartUnit); | 885 sendAnalysisNotificationHighlights(this, file, dartUnit); |
| 869 break; | 886 break; |
| 870 case AnalysisService.NAVIGATION: | 887 case AnalysisService.NAVIGATION: |
| 871 // TODO(scheglov) consider support for one unit in 2+ libraries | 888 // TODO(scheglov) consider support for one unit in 2+ libraries |
| 872 sendAnalysisNotificationNavigation(this, file, dartUnit); | 889 sendAnalysisNotificationNavigation(this, file, dartUnit); |
| 873 break; | 890 break; |
| 874 case AnalysisService.OCCURRENCES: | 891 case AnalysisService.OCCURRENCES: |
| 875 sendAnalysisNotificationOccurrences(this, file, dartUnit); | 892 sendAnalysisNotificationOccurrences(this, file, dartUnit); |
| 876 break; | 893 break; |
| 877 case AnalysisService.OUTLINE: | 894 case AnalysisService.OUTLINE: |
| 895 AnalysisContext context = dartUnit.element.context; | |
| 878 LineInfo lineInfo = context.getLineInfo(source); | 896 LineInfo lineInfo = context.getLineInfo(source); |
| 879 sendAnalysisNotificationOutline(this, file, lineInfo, dartUnit); | 897 sendAnalysisNotificationOutline(this, file, lineInfo, dartUnit); |
| 880 break; | 898 break; |
| 881 case AnalysisService.OVERRIDES: | 899 case AnalysisService.OVERRIDES: |
| 882 sendAnalysisNotificationOverrides(this, file, dartUnit); | 900 sendAnalysisNotificationOverrides(this, file, dartUnit); |
| 883 break; | 901 break; |
| 884 } | 902 } |
| 885 } | 903 } |
| 886 } | 904 } |
| 887 } | 905 } |
| 888 }); | 906 }); |
| 889 // remember new subscriptions | 907 // remember new subscriptions |
| 890 this.analysisServices = subscriptions; | 908 this.analysisServices = subscriptions; |
| 891 } | 909 } |
| 892 | 910 |
| 893 /** | 911 /** |
| 894 * Set the priority files to the given [files]. | 912 * Set the priority files to the given [files]. |
| 895 */ | 913 */ |
| 896 void setPriorityFiles(String requestId, List<String> files) { | 914 void setPriorityFiles(String requestId, List<String> files) { |
| 897 // Note: when a file is a priority file, that information needs to be | 915 // Note: when a file is a priority file, that information needs to be |
| 898 // propagated to all contexts that analyze the file, so that all contexts | 916 // propagated to all contexts that analyze the file, so that all contexts |
| 899 // will be able to do incremental resolution of the file. See | 917 // will be able to do incremental resolution of the file. See |
| 900 // dartbug.com/22209. | 918 // dartbug.com/22209. |
| 901 Map<AnalysisContext, List<Source>> sourceMap = | 919 Map<AnalysisContext, List<Source>> sourceMap = |
| 902 new HashMap<AnalysisContext, List<Source>>(); | 920 new HashMap<AnalysisContext, List<Source>>(); |
| 903 List<String> unanalyzed = new List<String>(); | 921 List<String> unanalyzed = new List<String>(); |
| 922 Source firstSource = null; | |
| 904 files.forEach((file) { | 923 files.forEach((file) { |
| 905 AnalysisContext preferredContext = getAnalysisContext(file); | 924 ContextSourcePair contextSource = getContextSourcePair(file); |
| 906 Source source = getSource(file); | 925 AnalysisContext preferredContext = contextSource.context; |
| 926 Source source = contextSource.source; | |
| 907 bool contextFound = false; | 927 bool contextFound = false; |
| 908 for (AnalysisContext context in folderMap.values) { | 928 for (AnalysisContext context in folderMap.values) { |
| 909 if (context == preferredContext || | 929 if (context == preferredContext || |
| 910 context.getKindOf(source) != SourceKind.UNKNOWN) { | 930 context.getKindOf(source) != SourceKind.UNKNOWN) { |
| 911 sourceMap.putIfAbsent(context, () => <Source>[]).add(source); | 931 sourceMap.putIfAbsent(context, () => <Source>[]).add(source); |
| 912 contextFound = true; | 932 contextFound = true; |
| 913 } | 933 } |
| 914 } | 934 } |
| 935 if (firstSource == null) { | |
| 936 firstSource = source; | |
| 937 } | |
| 915 if (!contextFound) { | 938 if (!contextFound) { |
| 916 unanalyzed.add(file); | 939 unanalyzed.add(file); |
| 917 } | 940 } |
| 918 }); | 941 }); |
| 919 if (unanalyzed.isNotEmpty) { | 942 if (unanalyzed.isNotEmpty) { |
| 920 StringBuffer buffer = new StringBuffer(); | 943 StringBuffer buffer = new StringBuffer(); |
| 921 buffer.writeAll(unanalyzed, ', '); | 944 buffer.writeAll(unanalyzed, ', '); |
| 922 throw new RequestFailure( | 945 throw new RequestFailure( |
| 923 new Response.unanalyzedPriorityFiles(requestId, buffer.toString())); | 946 new Response.unanalyzedPriorityFiles(requestId, buffer.toString())); |
| 924 } | 947 } |
| 925 folderMap.forEach((Folder folder, AnalysisContext context) { | 948 folderMap.forEach((Folder folder, AnalysisContext context) { |
| 926 List<Source> sourceList = sourceMap[context]; | 949 List<Source> sourceList = sourceMap[context]; |
| 927 if (sourceList == null) { | 950 if (sourceList == null) { |
| 928 sourceList = Source.EMPTY_ARRAY; | 951 sourceList = Source.EMPTY_ARRAY; |
| 929 } | 952 } |
| 930 context.analysisPriorityOrder = sourceList; | 953 context.analysisPriorityOrder = sourceList; |
| 931 // Schedule the context for analysis so that it has the opportunity to | 954 // Schedule the context for analysis so that it has the opportunity to |
| 932 // cache the AST's for the priority sources as soon as possible. | 955 // cache the AST's for the priority sources as soon as possible. |
| 933 schedulePerformAnalysisOperation(context); | 956 schedulePerformAnalysisOperation(context); |
| 934 }); | 957 }); |
| 935 operationQueue.reschedule(); | 958 operationQueue.reschedule(); |
| 936 Source firstSource = files.length > 0 ? getSource(files[0]) : null; | |
| 937 _onPriorityChangeController.add(new PriorityChangeEvent(firstSource)); | 959 _onPriorityChangeController.add(new PriorityChangeEvent(firstSource)); |
| 938 } | 960 } |
| 939 | 961 |
| 940 /** | 962 /** |
| 941 * Returns `true` if errors should be reported for [file] with the given | 963 * Returns `true` if errors should be reported for [file] with the given |
| 942 * absolute path. | 964 * absolute path. |
| 943 */ | 965 */ |
| 944 bool shouldSendErrorsNotificationFor(String file) { | 966 bool shouldSendErrorsNotificationFor(String file) { |
| 945 return !_noErrorNotification && | 967 return !_noErrorNotification && |
| 946 contextDirectoryManager.isInAnalysisRoot(file); | 968 contextDirectoryManager.isInAnalysisRoot(file); |
| 947 } | 969 } |
| 948 | 970 |
| 949 void shutdown() { | 971 void shutdown() { |
| 950 running = false; | 972 running = false; |
| 951 if (index != null) { | 973 if (index != null) { |
| 952 index.clear(); | 974 index.clear(); |
| 953 index.stop(); | 975 index.stop(); |
| 954 } | 976 } |
| 955 // Defer closing the channel and shutting down the instrumentation server so | 977 // Defer closing the channel and shutting down the instrumentation server so |
| 956 // that the shutdown response can be sent and logged. | 978 // that the shutdown response can be sent and logged. |
| 957 new Future(() { | 979 new Future(() { |
| 958 instrumentationService.shutdown(); | 980 instrumentationService.shutdown(); |
| 959 channel.close(); | 981 channel.close(); |
| 960 }); | 982 }); |
| 961 } | 983 } |
| 962 | 984 |
| 963 void test_flushResolvedUnit(String file) { | 985 void test_flushResolvedUnit(String file) { |
| 964 if (AnalysisEngine.isDartFileName(file)) { | 986 if (AnalysisEngine.isDartFileName(file)) { |
| 965 AnalysisContextImpl context = getAnalysisContext(file); | 987 ContextSourcePair contextSource = getContextSourcePair(file); |
| 966 Source source = getSource(file); | 988 AnalysisContextImpl context = contextSource.context; |
| 989 Source source = contextSource.source; | |
| 967 DartEntry dartEntry = context.getReadableSourceEntryOrNull(source); | 990 DartEntry dartEntry = context.getReadableSourceEntryOrNull(source); |
| 968 dartEntry.flushAstStructures(); | 991 dartEntry.flushAstStructures(); |
| 969 } | 992 } |
| 970 } | 993 } |
| 971 | 994 |
| 972 /** | 995 /** |
| 973 * Performs all scheduled analysis operations. | 996 * Performs all scheduled analysis operations. |
| 974 */ | 997 */ |
| 975 void test_performAllAnalysisOperations() { | 998 void test_performAllAnalysisOperations() { |
| 976 while (true) { | 999 while (true) { |
| 977 ServerOperation operation = operationQueue.takeIf((operation) { | 1000 ServerOperation operation = operationQueue.takeIf((operation) { |
| 978 return operation is PerformAnalysisOperation; | 1001 return operation is PerformAnalysisOperation; |
| 979 }); | 1002 }); |
| 980 if (operation == null) { | 1003 if (operation == null) { |
| 981 break; | 1004 break; |
| 982 } | 1005 } |
| 983 operation.perform(this); | 1006 operation.perform(this); |
| 984 } | 1007 } |
| 985 } | 1008 } |
| 986 | 1009 |
| 987 /** | 1010 /** |
| 988 * Implementation for `analysis.updateContent`. | 1011 * Implementation for `analysis.updateContent`. |
| 989 */ | 1012 */ |
| 990 void updateContent(String id, Map<String, dynamic> changes) { | 1013 void updateContent(String id, Map<String, dynamic> changes) { |
| 991 changes.forEach((file, change) { | 1014 changes.forEach((file, change) { |
| 992 Source source = getSource(file); | 1015 ContextSourcePair contextSource = getContextSourcePair(file); |
| 1016 Source source = contextSource.source; | |
| 993 operationQueue.sourceAboutToChange(source); | 1017 operationQueue.sourceAboutToChange(source); |
| 994 // Prepare the new contents. | 1018 // Prepare the new contents. |
| 995 String oldContents = overlayState.getContents(source); | 1019 String oldContents = overlayState.getContents(source); |
| 996 String newContents; | 1020 String newContents; |
| 997 if (change is AddContentOverlay) { | 1021 if (change is AddContentOverlay) { |
| 998 newContents = change.content; | 1022 newContents = change.content; |
| 999 } else if (change is ChangeContentOverlay) { | 1023 } else if (change is ChangeContentOverlay) { |
| 1000 if (oldContents == null) { | 1024 if (oldContents == null) { |
| 1001 // The client may only send a ChangeContentOverlay if there is | 1025 // The client may only send a ChangeContentOverlay if there is |
| 1002 // already an existing overlay for the source. | 1026 // already an existing overlay for the source. |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1066 // | 1090 // |
| 1067 // Update the defaults used to create new contexts. | 1091 // Update the defaults used to create new contexts. |
| 1068 // | 1092 // |
| 1069 AnalysisOptionsImpl options = contextDirectoryManager.defaultOptions; | 1093 AnalysisOptionsImpl options = contextDirectoryManager.defaultOptions; |
| 1070 optionUpdaters.forEach((OptionUpdater optionUpdater) { | 1094 optionUpdaters.forEach((OptionUpdater optionUpdater) { |
| 1071 optionUpdater(options); | 1095 optionUpdater(options); |
| 1072 }); | 1096 }); |
| 1073 } | 1097 } |
| 1074 | 1098 |
| 1075 /** | 1099 /** |
| 1076 * Return the [Source] of the Dart file with the given [path], assuming that | 1100 * Returns the [CompilationUnit] of the Dart file with the given [source] that |
| 1077 * we do not know the context in which the path should be interpreted. | 1101 * should be used to resend notifications for already resolved unit. |
| 1102 * Returns `null` if the file is not a part of any context, library has not | |
| 1103 * been yet resolved, or any problem happened. | |
| 1078 */ | 1104 */ |
| 1079 Source _getSourceWithoutContext(String path) { | 1105 CompilationUnit _getResolvedCompilationUnitToResendNotification( |
| 1080 // try SDK | 1106 AnalysisContext context, Source source) { |
| 1081 { | 1107 List<Source> librarySources = context.getLibrariesContaining(source); |
| 1082 Uri uri = resourceProvider.pathContext.toUri(path); | 1108 if (librarySources.isEmpty) { |
| 1083 Source sdkSource = defaultSdk.fromFileUri(uri); | 1109 return null; |
| 1084 if (sdkSource != null) { | |
| 1085 return sdkSource; | |
| 1086 } | |
| 1087 } | 1110 } |
| 1088 // file-based source | 1111 // if library has not been resolved yet, the unit will be resolved later |
| 1089 File file = resourceProvider.getResource(path); | 1112 Source librarySource = librarySources[0]; |
| 1090 return file.createSource(); | 1113 if (context.getLibraryElement(librarySource) == null) { |
| 1114 return null; | |
| 1115 } | |
| 1116 // if library has been already resolved, resolve unit | |
| 1117 return context.resolveCompilationUnit2(source, librarySource); | |
| 1091 } | 1118 } |
| 1092 | 1119 |
| 1093 /** | 1120 /** |
| 1094 * Schedules [performOperation] exection. | 1121 * Schedules [performOperation] exection. |
| 1095 */ | 1122 */ |
| 1096 void _schedulePerformOperation() { | 1123 void _schedulePerformOperation() { |
| 1097 if (performOperationPending) { | 1124 if (performOperationPending) { |
| 1098 return; | 1125 return; |
| 1099 } | 1126 } |
| 1100 /* | 1127 /* |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1148 /** | 1175 /** |
| 1149 * The contexts that were removed from the server. | 1176 * The contexts that were removed from the server. |
| 1150 */ | 1177 */ |
| 1151 final List<AnalysisContext> removed; | 1178 final List<AnalysisContext> removed; |
| 1152 | 1179 |
| 1153 ContextsChangedEvent({this.added: AnalysisContext.EMPTY_LIST, | 1180 ContextsChangedEvent({this.added: AnalysisContext.EMPTY_LIST, |
| 1154 this.changed: AnalysisContext.EMPTY_LIST, | 1181 this.changed: AnalysisContext.EMPTY_LIST, |
| 1155 this.removed: AnalysisContext.EMPTY_LIST}); | 1182 this.removed: AnalysisContext.EMPTY_LIST}); |
| 1156 } | 1183 } |
| 1157 | 1184 |
| 1185 class ContextSourcePair { | |
|
Brian Wilkerson
2015/03/06 15:04:27
Comments?
scheglov
2015/03/06 21:47:03
Done.
| |
| 1186 final AnalysisContext context; | |
| 1187 final Source source; | |
| 1188 ContextSourcePair(this.context, this.source); | |
| 1189 } | |
| 1190 | |
| 1158 /** | 1191 /** |
| 1159 * A [PriorityChangeEvent] indicates the set the priority files has changed. | 1192 * A [PriorityChangeEvent] indicates the set the priority files has changed. |
| 1160 */ | 1193 */ |
| 1161 class PriorityChangeEvent { | 1194 class PriorityChangeEvent { |
| 1162 final Source firstSource; | 1195 final Source firstSource; |
| 1163 | 1196 |
| 1164 PriorityChangeEvent(this.firstSource); | 1197 PriorityChangeEvent(this.firstSource); |
| 1165 } | 1198 } |
| 1166 | 1199 |
| 1167 class ServerContextManager extends ContextManager { | 1200 class ServerContextManager extends ContextManager { |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1364 /** | 1397 /** |
| 1365 * The [PerformanceTag] for time spent in server request handlers. | 1398 * The [PerformanceTag] for time spent in server request handlers. |
| 1366 */ | 1399 */ |
| 1367 static PerformanceTag serverRequests = new PerformanceTag('serverRequests'); | 1400 static PerformanceTag serverRequests = new PerformanceTag('serverRequests'); |
| 1368 | 1401 |
| 1369 /** | 1402 /** |
| 1370 * The [PerformanceTag] for time spent in split store microtasks. | 1403 * The [PerformanceTag] for time spent in split store microtasks. |
| 1371 */ | 1404 */ |
| 1372 static PerformanceTag splitStore = new PerformanceTag('splitStore'); | 1405 static PerformanceTag splitStore = new PerformanceTag('splitStore'); |
| 1373 } | 1406 } |
| OLD | NEW |