GoToImplementationCommandHandler.cs 6.3 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Editor.Commands;
9
using Microsoft.CodeAnalysis.Editor.FindUsages;
10 11 12 13
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Notification;
C
CyrusNajmabadi 已提交
14
using Microsoft.CodeAnalysis.Shared.Extensions;
15 16 17 18 19 20
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.Editor.GoToImplementation
{
    [ExportCommandHandler(PredefinedCommandHandlerNames.GoToImplementation,
        ContentTypeNames.RoslynContentType)]
21
    internal partial class GoToImplementationCommandHandler : ICommandHandler<GoToImplementationCommandArgs>
22 23 24 25 26 27 28
    {
        private readonly IWaitIndicator _waitIndicator;
        private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;

        [ImportingConstructor]
        public GoToImplementationCommandHandler(
            IWaitIndicator waitIndicator,
C
CyrusNajmabadi 已提交
29
            [ImportMany] IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
        {
            _waitIndicator = waitIndicator;
            _streamingPresenters = streamingPresenters;
        }

        public CommandState GetCommandState(GoToImplementationCommandArgs args, Func<CommandState> nextHandler)
        {
            // Because this is expensive to compute, we just always say yes
            return CommandState.Available;
        }

        public void ExecuteCommand(GoToImplementationCommandArgs args, Action nextHandler)
        {
            var caret = args.TextView.GetCaretPoint(args.SubjectBuffer);

            if (caret.HasValue)
            {
                var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
                if (document != null)
                {
                    ExecuteCommand(document, caret.Value);
                    return;
                }
            }

            nextHandler();
        }

        private void ExecuteCommand(Document document, int caretPosition)
        {
60
            var streamingService = document.GetLanguageService<IFindUsagesService>();
C
CyrusNajmabadi 已提交
61
            var synchronousService = document.GetLanguageService<IGoToImplementationService>();
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

            var streamingPresenter = GetStreamingPresenter();

            // See if we're running on a host that can provide streaming results.
            // We'll both need a FAR service that can stream results to us, and 
            // a presenter that can accept streamed results.
            var streamingEnabled = document.Project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.StreamingGoToImplementation, document.Project.Language);
            var canUseStreamingWindow = streamingEnabled && streamingService != null && streamingPresenter != null;
            var canUseSynchronousWindow = synchronousService != null;

            if (canUseStreamingWindow || canUseSynchronousWindow)
            {
                // We have all the cheap stuff, so let's do expensive stuff now
                string messageToShow = null;
                _waitIndicator.Wait(
                    EditorFeaturesResources.Go_To_Implementation,
                    EditorFeaturesResources.Locating_implementations,
                    allowCancel: true,
                    action: context =>
                    {
                        if (canUseStreamingWindow)
                        {
                            StreamingGoToImplementation(
                                document, caretPosition,
86
                                streamingService, streamingPresenter,
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
                                context.CancellationToken, out messageToShow);
                        }
                        else
                        {
                            synchronousService.TryGoToImplementation(
                                document, caretPosition, context.CancellationToken, out messageToShow);
                        }
                    });

                if (messageToShow != null)
                {
                    var notificationService = document.Project.Solution.Workspace.Services.GetService<INotificationService>();
                    notificationService.SendNotification(messageToShow,
                        title: EditorFeaturesResources.Go_To_Implementation,
                        severity: NotificationSeverity.Information);
                }
            }
        }

        private void StreamingGoToImplementation(
            Document document, int caretPosition,
108
            IFindUsagesService findUsagesService,
109 110 111 112
            IStreamingFindUsagesPresenter streamingPresenter,
            CancellationToken cancellationToken,
            out string messageToShow)
        {
C
CyrusNajmabadi 已提交
113 114 115 116
            // We create our own context object, simply to capture all the definitions reported by 
            // the individual IFindUsagesService.  Once we get the results back we'll then decide 
            // what to do with them.  If we get only a single result back, then we'll just go 
            // directly to it.  Otherwise, we'll present the results in the IStreamingFindUsagesPresenter.
117
            var goToImplContext = new SimpleFindUsagesContext(cancellationToken);
118
            findUsagesService.FindImplementationsAsync(document, caretPosition, goToImplContext).Wait(cancellationToken);
119 120 121 122 123 124 125 126 127

            // If finding implementations reported a message, then just stop and show that 
            // message to the user.
            messageToShow = goToImplContext.Message;
            if (messageToShow != null)
            {
                return;
            }

128
            var definitionItems = goToImplContext.GetDefinitions();
129

130
            streamingPresenter.NavigateToOrPresentItemsAsync(
131
                EditorFeaturesResources.Go_To_Implementation, definitionItems).Wait(cancellationToken);
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
        }

        private IStreamingFindUsagesPresenter GetStreamingPresenter()
        {
            try
            {
                return _streamingPresenters.FirstOrDefault()?.Value;
            }
            catch
            {
                return null;
            }
        }
    }
}