# Providing Navigation

Navigation information is used by clients to allow users to navigate to the
location at which an identifier is defined.

## Implementation details

Navigation information can be requested both by an `analysis.getNavigation`
request and by a subscription. If the server has subscribed for navigation
information in some set of files, the plugin should send the information in
an `analysis.navigation` notification whenever the information needs to be
updated.

When an `analysis.getNavigation` request is received, the method
`handleAnalysisGetNavigation` will be invoked. This method is responsible for
returning a response that contains the available navigation information.

When a notification needs to be sent, the method `sendNavigationNotification`
will be invoked. This method is responsible for sending the notification.

The easiest way to add support for both the request and the notification is by
adding the classes `NavigationMixin` and `DartNavigationMixin` (from
`package:analyzer_plugin/plugin/navigation_mixin.dart`) to the list of mixins
for your subclass of `ServerPlugin`. This will leave you with one abstract
method that you need to implement: `getNavigationContributors`. That method is
responsible for returning a list of `NavigationContributor`s. It is the
navigation contributors that produce the actual navigation information. (Most
plugins will only need a single navigation contributor.)

To write a navigation contributor, create a class that implements
`NavigationContributor`. The interface defines a single method named
`computeNavigation`. The method has two arguments: a `NavigationRequest` that
describes the region of the file for which navigation is being requested and a
`NavigationCollector` through which navigation information is to be added.

If you mix in the class `DartNavigationMixin`, then the request will be an
instance of `DartNavigationRequest`, which also has analysis results.

## Example

Start by creating a class that implements `NavigationContributor`, then
implement the method `computeNavigation`. This method is typically implemented
by creating a visitor (such as an AstVisitor) that can visit the results of the
analysis (such as a CompilationUnit) and extract the navigation information from
the analysis result.

For example, your contributor might look something like the following:

```dart
class MyNavigationContributor implements NavigationContributor {
  @override
  void computeNavigation(
      NavigationRequest request, NavigationCollector collector) {
    if (request is DartNavigationRequest) {
      NavigationVisitor visitor = new NavigationVisitor(collector);
      request.result.unit.accept(visitor);
    }
  }
}

class NavigationVisitor extends RecursiveAstVisitor {
  final NavigationCollector collector;

  NavigationVisitor(this.collector);

  @override
  void visitSimpleIdentifier(SimpleIdentifier node) {
    // ...
  }
}
```

Given a contributor like the one above, you can implement your plugin similar to
the following:

```dart
class MyPlugin extends ServerPlugin with NavigationMixin, DartNavigationMixin {
  // ...

  @override
  List<NavigationContributor> getNavigationContributors(String path) {
    return <NavigationContributor>[new MyNavigationContributor()];
  }
}
```
