| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library domains.analysis.navigation; |
| |
| import 'dart:collection'; |
| |
| import 'package:analysis_server/plugin/analysis/navigation/navigation_core.dart'; |
| import 'package:analysis_server/src/analysis_server.dart'; |
| import 'package:analysis_server/src/collections.dart'; |
| import 'package:analysis_server/src/protocol_server.dart' as protocol; |
| import 'package:analyzer/exception/exception.dart'; |
| import 'package:analyzer/src/generated/engine.dart' |
| show AnalysisContext, AnalysisEngine; |
| import 'package:analyzer/src/generated/source.dart' show Source, SourceRange; |
| |
| /** |
| * Compute all known navigation information for the given part of [source]. |
| */ |
| NavigationCollectorImpl computeNavigation(AnalysisServer server, |
| AnalysisContext context, Source source, int offset, int length) { |
| NavigationCollectorImpl collector = new NavigationCollectorImpl(); |
| List<NavigationContributor> contributors = |
| server.serverPlugin.navigationContributors; |
| for (NavigationContributor contributor in contributors) { |
| try { |
| contributor.computeNavigation(collector, context, source, offset, length); |
| } catch (exception, stackTrace) { |
| AnalysisEngine.instance.logger.logError( |
| 'Exception from navigation contributor: ${contributor.runtimeType}', |
| new CaughtException(exception, stackTrace)); |
| } |
| } |
| collector.createRegions(); |
| return collector; |
| } |
| |
| /** |
| * A concrete implementation of [NavigationCollector]. |
| */ |
| class NavigationCollectorImpl implements NavigationCollector { |
| /** |
| * A list of navigation regions. |
| */ |
| final List<protocol.NavigationRegion> regions = <protocol.NavigationRegion>[]; |
| final Map<SourceRange, List<int>> regionMap = |
| new HashMap<SourceRange, List<int>>(); |
| |
| /** |
| * All the unique targets referenced by [regions]. |
| */ |
| final List<protocol.NavigationTarget> targets = <protocol.NavigationTarget>[]; |
| final Map<Pair<protocol.ElementKind, protocol.Location>, int> targetMap = |
| new HashMap<Pair<protocol.ElementKind, protocol.Location>, int>(); |
| |
| /** |
| * All the unique files referenced by [targets]. |
| */ |
| final List<String> files = <String>[]; |
| final Map<String, int> fileMap = new HashMap<String, int>(); |
| |
| @override |
| void addRegion(int offset, int length, protocol.ElementKind targetKind, |
| protocol.Location targetLocation) { |
| SourceRange range = new SourceRange(offset, length); |
| // prepare targets |
| List<int> targets = regionMap[range]; |
| if (targets == null) { |
| targets = <int>[]; |
| regionMap[range] = targets; |
| } |
| // add new target |
| int targetIndex = _addTarget(targetKind, targetLocation); |
| targets.add(targetIndex); |
| } |
| |
| void createRegions() { |
| regionMap.forEach((range, targets) { |
| protocol.NavigationRegion region = |
| new protocol.NavigationRegion(range.offset, range.length, targets); |
| regions.add(region); |
| }); |
| regions.sort((a, b) { |
| return a.offset - b.offset; |
| }); |
| } |
| |
| int _addFile(String file) { |
| int index = fileMap[file]; |
| if (index == null) { |
| index = files.length; |
| files.add(file); |
| fileMap[file] = index; |
| } |
| return index; |
| } |
| |
| int _addTarget(protocol.ElementKind kind, protocol.Location location) { |
| var pair = |
| new Pair<protocol.ElementKind, protocol.Location>(kind, location); |
| int index = targetMap[pair]; |
| if (index == null) { |
| String file = location.file; |
| int fileIndex = _addFile(file); |
| index = targets.length; |
| protocol.NavigationTarget target = new protocol.NavigationTarget( |
| kind, |
| fileIndex, |
| location.offset, |
| location.length, |
| location.startLine, |
| location.startColumn); |
| targets.add(target); |
| targetMap[pair] = index; |
| } |
| return index; |
| } |
| } |