Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Roslyn.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31319.15
# Visual Studio Version 18
VisualStudioVersion = 18.0.10623.112 main
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RoslynDeployment", "src\Deployment\RoslynDeployment.csproj", "{600AF682-E097-407B-AD85-EE3CED37E680}"
EndProject
Expand Down Expand Up @@ -2207,8 +2207,11 @@ Global
src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\VisualBasicWorkspaceExtensions.projitems*{57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c}*SharedItemsImports = 5
src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{58969243-7f59-4236-93d0-c93b81f569b3}*SharedItemsImports = 13
src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5
src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5
src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5
src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5
src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\CompilerExtensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5
src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Extensions\Microsoft.CodeAnalysis.Extensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5
src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\WorkspaceExtensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5
src\Analyzers\Core\CodeFixes\CodeFixes.projitems*{5ff1e493-69cc-4d0b-83f2-039f469a04e1}*SharedItemsImports = 5
src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\WorkspaceExtensions.projitems*{5ff1e493-69cc-4d0b-83f2-039f469a04e1}*SharedItemsImports = 5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,19 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.ComponentModel.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.GoToDefinition;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Microsoft.VisualStudio.Utilities;

Expand All @@ -29,131 +23,37 @@ namespace Microsoft.CodeAnalysis.FindReferences;
[Export(typeof(ICommandHandler))]
[ContentType(ContentTypeNames.RoslynContentType)]
[Name(PredefinedCommandHandlerNames.FindReferences)]
internal sealed class FindReferencesCommandHandler : ICommandHandler<FindReferencesCommandArgs>
[method: ImportingConstructor]
[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
internal sealed class FindReferencesCommandHandler(
IThreadingContext threadingContext,
IStreamingFindUsagesPresenter streamingPresenter,
IAsynchronousOperationListenerProvider listenerProvider,
IGlobalOptionService globalOptions) : AbstractGoOrFindCommandHandler<IFindUsagesService, FindReferencesCommandArgs>(
threadingContext,
streamingPresenter,
listenerProvider.GetListener(FeatureAttribute.FindReferences),
globalOptions)
{
private readonly IStreamingFindUsagesPresenter _streamingPresenter;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant with teh code in AbstractGoOrFindCommandHandler.

private readonly IGlobalOptionService _globalOptions;
private readonly IAsynchronousOperationListener _asyncListener;

public string DisplayName => EditorFeaturesResources.Find_References;

[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
public FindReferencesCommandHandler(
IStreamingFindUsagesPresenter streamingPresenter,
IGlobalOptionService globalOptions,
IAsynchronousOperationListenerProvider listenerProvider)
{
Contract.ThrowIfNull(listenerProvider);

_streamingPresenter = streamingPresenter;
_globalOptions = globalOptions;
_asyncListener = listenerProvider.GetListener(FeatureAttribute.FindReferences);
}

public CommandState GetCommandState(FindReferencesCommandArgs args)
{
var (_, service) = GetDocumentAndService(args.SubjectBuffer.CurrentSnapshot);
return service != null
? CommandState.Available
: CommandState.Unspecified;
}

public bool ExecuteCommand(FindReferencesCommandArgs args, CommandExecutionContext context)
{
var subjectBuffer = args.SubjectBuffer;

// Get the selection that user has in our buffer (this also works if there
// is no selection and the caret is just at a single position). If we
// can't get the selection, or there are multiple spans for it (i.e. a
// box selection), then don't do anything.
var snapshotSpans = args.TextView.Selection.GetSnapshotSpansOnBuffer(subjectBuffer);
if (snapshotSpans.Count == 1)
{
var selectedSpan = snapshotSpans[0];
var (document, service) = GetDocumentAndService(subjectBuffer.CurrentSnapshot);
if (document != null)
{
// Do a find-refs at the *start* of the selection. That way if the
// user has selected a symbol that has another symbol touching it
// on the right (i.e. Goo++ ), then we'll do the find-refs on the
// symbol selected, not the symbol following.
if (TryExecuteCommand(selectedSpan.Start, document, service))
{
return true;
}
}
}

return false;
}
public override string DisplayName => EditorFeaturesResources.Find_References;

private static (Document, IFindUsagesService) GetDocumentAndService(ITextSnapshot snapshot)
{
var document = snapshot.GetOpenDocumentInCurrentContextWithChanges();
return (document, document?.GetLanguageService<IFindUsagesService>());
}

private bool TryExecuteCommand(int caretPosition, Document document, IFindUsagesService findUsagesService)
{
// 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.
if (findUsagesService != null && _streamingPresenter != null)
{
// kick this work off in a fire and forget fashion. Importantly, this means we do
// not pass in any ambient cancellation information as the execution of this command
// will complete and will have no bearing on the computation of the references we compute.
_ = StreamingFindReferencesAsync(document, caretPosition, findUsagesService, _streamingPresenter);
return true;
}
protected override FunctionId FunctionId => FunctionId.CommandHandler_FindAllReference;

return false;
}
/// <summary>
/// For find-refs, we *always* use the window. Even if there is only a single result. This is not a 'go' command
/// which imperatively tries to navigate to the location if possible. The intent here is to keep the results in view
/// so that the user can always refer to them, even as they do other work.
/// </summary>
protected override bool NavigateToSingleResultIfQuick => false;

private async Task StreamingFindReferencesAsync(
Document document,
int caretPosition,
IFindUsagesService findUsagesService,
IStreamingFindUsagesPresenter presenter)
{
try
protected override StreamingFindUsagesPresenterOptions GetStreamingPresenterOptions(Document document)
=> new()
{
using var token = _asyncListener.BeginAsyncOperation(nameof(StreamingFindReferencesAsync));
var classificationOptions = _globalOptions.GetClassificationOptionsProvider();
SupportsReferences = true,
IncludeContainingTypeAndMemberColumns = document.Project.SupportsCompilation,
IncludeKindColumn = document.Project.Language != LanguageNames.FSharp
};

// Let the presented know we're starting a search. It will give us back the context object that the FAR
// service will push results into. This operation is not externally cancellable. Instead, the find refs
// window will cancel it if another request is made to use it.
var (context, cancellationToken) = presenter.StartSearch(
EditorFeaturesResources.Find_References,
new StreamingFindUsagesPresenterOptions()
{
SupportsReferences = true,
IncludeContainingTypeAndMemberColumns = document.Project.SupportsCompilation,
IncludeKindColumn = document.Project.Language != LanguageNames.FSharp
});

using (Logger.LogBlock(
FunctionId.CommandHandler_FindAllReference,
KeyValueLogMessage.Create(LogType.UserAction, static m => m["type"] = "streaming"),
cancellationToken))
{
try
{
await findUsagesService.FindReferencesAsync(context, document, caretPosition, classificationOptions, cancellationToken).ConfigureAwait(false);
}
finally
{
await context.OnCompletedAsync(cancellationToken).ConfigureAwait(false);
}
}
}
catch (OperationCanceledException)
{
}
catch (Exception e) when (FatalError.ReportAndCatch(e))
{
}
}
protected override Task FindActionAsync(IFindUsagesContext context, Document document, IFindUsagesService service, int caretPosition, CancellationToken cancellationToken)
=> service.FindReferencesAsync(context, document, caretPosition, this.ClassificationOptionsProvider, cancellationToken);
}
19 changes: 15 additions & 4 deletions src/EditorFeatures/Core/FindUsages/BufferedFindUsagesContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;

Expand All @@ -28,6 +29,7 @@ private sealed class State
public string? InformationalMessage;
public string? SearchTitle;
public ImmutableArray<DefinitionItem>.Builder Definitions = ImmutableArray.CreateBuilder<DefinitionItem>();
public ImmutableArray<SourceReferenceItem>.Builder References = ImmutableArray.CreateBuilder<SourceReferenceItem>();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed as technically FAR is handed the buffered progress guy and may start adding references to it before we swap it out to use the real FAR window.

}

/// <summary>
Expand Down Expand Up @@ -108,6 +110,8 @@ public async Task AttachToStreamingPresenterAsync(IFindUsagesContext presenterCo
foreach (var definition in _state.Definitions)
await presenterContext.OnDefinitionFoundAsync(definition, cancellationToken).ConfigureAwait(false);

await presenterContext.OnReferencesFoundAsync(_state.References.AsAsyncEnumerable(), cancellationToken).ConfigureAwait(false);

// Now swap over to the presenter being the sink for all future callbacks, and clear any buffered data.
_streamingPresenterContext = presenterContext;
_state = null;
Expand Down Expand Up @@ -199,11 +203,18 @@ async ValueTask IFindUsagesContext.OnDefinitionFoundAsync(DefinitionItem definit
}
}

ValueTask IFindUsagesContext.OnReferencesFoundAsync(IAsyncEnumerable<SourceReferenceItem> references, CancellationToken cancellationToken)
async ValueTask IFindUsagesContext.OnReferencesFoundAsync(IAsyncEnumerable<SourceReferenceItem> references, CancellationToken cancellationToken)
{
// Entirely ignored. These features do not show references.
Contract.Fail("GoToImpl/Base should never report a reference.");
return ValueTaskFactory.CompletedTask;
using var _ = await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false);
if (IsSwapped)
{
await _streamingPresenterContext.OnReferencesFoundAsync(references, cancellationToken).ConfigureAwait(false);
}
else
{
await foreach (var reference in references)
_state.References.Add(reference);
}
}

#endregion
Expand Down
24 changes: 13 additions & 11 deletions src/EditorFeatures/Core/GoToBase/GoToBaseCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Microsoft.VisualStudio.Utilities;
Expand All @@ -30,20 +29,23 @@ namespace Microsoft.CodeAnalysis.GoToBase;
internal sealed class GoToBaseCommandHandler(
IThreadingContext threadingContext,
IStreamingFindUsagesPresenter streamingPresenter,
IUIThreadOperationExecutor uiThreadOperationExecutor,
IAsynchronousOperationListenerProvider listenerProvider,
IGlobalOptionService globalOptions) : AbstractGoToCommandHandler<IGoToBaseService, GoToBaseCommandArgs>(threadingContext,
streamingPresenter,
uiThreadOperationExecutor,
listenerProvider.GetListener(FeatureAttribute.GoToBase),
globalOptions)
IGlobalOptionService globalOptions) : AbstractGoOrFindCommandHandler<IGoToBaseService, GoToBaseCommandArgs>(
threadingContext,
streamingPresenter,
listenerProvider.GetListener(FeatureAttribute.GoToBase),
globalOptions)
{
public override string DisplayName => EditorFeaturesResources.Go_To_Base;

protected override string ScopeDescription => EditorFeaturesResources.Locating_bases;
protected override FunctionId FunctionId => FunctionId.CommandHandler_GoToBase;

protected override Task FindActionAsync(IFindUsagesContext context, Document document, int caretPosition, CancellationToken cancellationToken)
=> document.GetRequiredLanguageService<IGoToBaseService>()
.FindBasesAsync(context, document, caretPosition, ClassificationOptionsProvider, cancellationToken);
/// <summary>
/// If we find a single results quickly enough, we do want to take the user directly to it,
/// instead of popping up the FAR window to show it.
/// </summary>
protected override bool NavigateToSingleResultIfQuick => true;

protected override Task FindActionAsync(IFindUsagesContext context, Document document, IGoToBaseService service, int caretPosition, CancellationToken cancellationToken)
=> service.FindBasesAsync(context, document, caretPosition, ClassificationOptionsProvider, cancellationToken);
}
Loading
Loading