Page Comparison - Navigation (v.7 vs v.8)

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

ReSharper navigation consists of two main parts:

  • Context navigation ('Navigate From Here...')
  • Global navigation ('Go To ...')

The navigation framework is fairly pluggable, so in most cases all you need to do is to simply provide your own components. You can implement the following features:

Table of Contents

Context navigation consists of two parts: the searching engine and the user-interaction provider. These two parts are divided into different entities in ReSharper Framework.

Feature providers represent features themselves. That is, there is just one provider per one navigation feature regardless of number of languages and environments where does this feature is supposed to work.

Each context navigation provider implements either the IContextSearchProvider or the INavigateFromHereProvider interface. The IContextSearchProvider interface is defined as follows:

Code Block
languagecsharp
public interface IContextSearchProvider
{
  [CanBeNull]
  Action GetSearchesExecution(IDataContext dataContext, INavigationExecutionHost host);
}

The action that the GetSearchesExecution() method returns is actually the execution of the navigation feature. When this method returns null, it implies that this feature is not available for this data context. The INavigationExecutionHost is a host object which can be used for performing certain UI activities, such as showing a drop-down box or the advanced search dialog.
The INavigateFromHere interface is fairly similar to IContextSearchProvider -- it has just one method that returns navigation execution and additional data for presenting in 'Navigate From Here' drop-down menu.

If you want your navigation items to appear in the 'Navigate From Here' menu, you have to implement the INavigateFromHereProvider interface.

You don't need to write any code in your action handler's methods, as all feature logic needs to be written inside navigation providers. All you need to bind your action to a navigation provider is to derive your custom action handler class from ContextNavigationActionBase<TNavigateFromHereProvider> (if you want your action to appear in the 'Navigate From Here' menu) or ContextSearchActionBase<TContextSearchProvider>.
Additionally, your provider should be decorated with the ContextNavigationProvider attribute.

Tip

If you want your feature to appear only in 'Navigate From Here' menu, you don't have to even write an action - just implement ContextNavigationProvider that implemenets INavigateFromHereProvider.

If your navigation is not complicated, that's all you need to know about the context navigation framework.

Here is an example of simple navigation to the corresponding folder using Windows explorer.

Code Block
languagecsharp
using System;
using System.Collections.Generic;
using System.Diagnostics;
using JetBrains.Annotations;
using JetBrains.Application.DataContext;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.ContextNavigation;
using JetBrains.Util;

using DataConstants = JetBrains.ProjectModel.DataContext.DataConstants;

namespace ReSharperPlugIn6
{
  [ActionHandler]
  public class SimpleNavigation : ContextNavigationActionBase<SimpleNavigationProvider>
  {

  }

  [ContextNavigationProvider]
  public class SimpleNavigationProvider : INavigateFromHereProvider
  {
    [CanBeNull]
    private static ProcessStartInfo GetProcessStartInfo([NotNull] FileSystemPath path)
    {
      return new ProcessStartInfo("explorer.exe",
        path.ExistsFile ? string.Format(@"/select,""{0}""", path) : string.Format(@"""{0}""", path.Directory));
    }

    [CanBeNull]
    private static FileSystemPath GetPathByContext([NotNull] IDataContext context)
    {
      var projectModelElement = context.GetData(DataConstants.PROJECT_MODEL_ELEMENT);

      var projectItem = projectModelElement as IProjectItem;
      if (projectItem == null)
        return null;

      if (!projectItem.Location.Directory.ExistsDirectory)
        return null;

      return projectItem.Location;
    }

    public IEnumerable<ContextNavigation> CreateWorkflow(IDataContext dataContext)
    {
      var path = GetPathByContext(dataContext);
      if (path != null)
      {
        ProcessStartInfo processStartInfo = GetProcessStartInfo(path);
        if (processStartInfo != null)
        {
          yield return new ContextNavigation(
            "&Windows Explorer",
            null,
            NavigationActionGroup.Other,
            () =>
            {
              try
              {
                using (Process.Start(processStartInfo)) { }
              }
              catch (Exception e)
              {
                MessageBox.ShowError(e.Message);
              }
            });
        }
      }
    }

  }
}

If your navigation is complicated and works differently in differenent languages then you can use context searches to provide different search results for a specific language.
In this case, your provider can be derived from ContextSearchesCollector<TContextSearch> class and all you need is to implement the Execute() method.

Code Block
languagecsharp
protected abstract void Execute(IDataContext dataContext, IEnumerable<TContextSearch> searches, INavigationExecutionHost host);

IContextSearch is an interface for providers decorated with the FeaturePart attribute and providing specific navigation execution. There are just two methods in this interface, indicating applicability and availability of said context search.

Code Block
languagecsharp
public interface IContextSearch
{
  bool IsAvailable(IDataContext dataContext);
  bool IsApplicable(IDataContext dataContext);
}

The applicability method is more global and implies that this context search can work for this data context and will override other components it is derived from.
Availability means that for a specific data context this context search is enabled and will be fired.

You can alter the behavior of existing ReSharper navigation features by providing your own context search components. Here is an example of a  context search that extends the 'Go To Implementation' search for XAML:

Code Block
languagecsharp
[FeaturePart]
public class XamlImplementationContextSearch : ContextNavigation.ContextSearches.BaseSearches.ImplementationContextSearch
{
  protected override bool IsAvailable(IDataContext dataContext)
  {
    return false;
  }

  public override bool IsApplicable(IDataContext dataContext)
  {
    return ContextNavigationUtil.CheckDefaultApplicability<XamlLanguage>(dataContext);
  }

  protected override SearchImplementationsRequest CreateSearchRequest(IDataContext dataContext, IDeclaredElement declaredElement)
  {
    //YOUR CUSTOM LOGIC HERE...
  }
}

Example navigation plug-ins.