22// The .NET Foundation licenses this file to you under the MIT license.
33// See the LICENSE file in the project root for more information.
44
5- #nullable disable
6-
7- using System ;
85using System . ComponentModel . Composition ;
96using System . Diagnostics . CodeAnalysis ;
7+ using System . Threading ;
108using System . Threading . Tasks ;
11- using Microsoft . CodeAnalysis . Classification ;
129using Microsoft . CodeAnalysis . Editor ;
1310using Microsoft . CodeAnalysis . Editor . Host ;
14- using Microsoft . CodeAnalysis . Editor . Shared . Extensions ;
15- using Microsoft . CodeAnalysis . ErrorReporting ;
11+ using Microsoft . CodeAnalysis . Editor . Shared . Utilities ;
1612using Microsoft . CodeAnalysis . FindUsages ;
1713using Microsoft . CodeAnalysis . GoToDefinition ;
1814using Microsoft . CodeAnalysis . Internal . Log ;
1915using Microsoft . CodeAnalysis . Options ;
20- using Microsoft . CodeAnalysis . Shared . Extensions ;
2116using Microsoft . CodeAnalysis . Shared . TestHooks ;
22- using Microsoft . CodeAnalysis . Text ;
2317using Microsoft . VisualStudio . Commanding ;
24- using Microsoft . VisualStudio . Text ;
2518using Microsoft . VisualStudio . Text . Editor . Commanding . Commands ;
2619using Microsoft . VisualStudio . Utilities ;
2720
@@ -30,134 +23,37 @@ namespace Microsoft.CodeAnalysis.FindReferences;
3023[ Export ( typeof ( ICommandHandler ) ) ]
3124[ ContentType ( ContentTypeNames . RoslynContentType ) ]
3225[ Name ( PredefinedCommandHandlerNames . FindReferences ) ]
33- internal sealed class FindReferencesCommandHandler : AbstractGoToCommandHandler <
34- IFindUsagesService , FindReferencesCommandArgs >
26+ [ method: ImportingConstructor ]
27+ [ method: SuppressMessage ( "RoslynDiagnosticsReliability" , "RS0033:Importing constructor should be [Obsolete]" , Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814" ) ]
28+ internal sealed class FindReferencesCommandHandler (
29+ IThreadingContext threadingContext ,
30+ IStreamingFindUsagesPresenter streamingPresenter ,
31+ IAsynchronousOperationListenerProvider listenerProvider ,
32+ IGlobalOptionService globalOptions ) : AbstractGoOrFindCommandHandler < IFindUsagesService , FindReferencesCommandArgs > (
33+ threadingContext ,
34+ streamingPresenter ,
35+ listenerProvider . GetListener ( FeatureAttribute . FindReferences ) ,
36+ globalOptions )
3537{
36- #if false
37- private readonly IStreamingFindUsagesPresenter _streamingPresenter ;
38- private readonly IGlobalOptionService _globalOptions ;
39- private readonly IAsynchronousOperationListener _asyncListener ;
40-
41- public string DisplayName => EditorFeaturesResources . Find_References ;
42-
43- [ ImportingConstructor ]
44- [ SuppressMessage ( "RoslynDiagnosticsReliability" , "RS0033:Importing constructor should be [Obsolete]" , Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814" ) ]
45- public FindReferencesCommandHandler (
46- IStreamingFindUsagesPresenter streamingPresenter ,
47- IGlobalOptionService globalOptions ,
48- IAsynchronousOperationListenerProvider listenerProvider )
49- {
50- Contract . ThrowIfNull ( listenerProvider ) ;
51-
52- _streamingPresenter = streamingPresenter ;
53- _globalOptions = globalOptions ;
54- _asyncListener = listenerProvider . GetListener ( FeatureAttribute . FindReferences ) ;
55- }
56-
57- public CommandState GetCommandState ( FindReferencesCommandArgs args )
58- {
59- var ( _, service ) = GetDocumentAndService ( args . SubjectBuffer . CurrentSnapshot ) ;
60- return service != null
61- ? CommandState . Available
62- : CommandState . Unspecified ;
63- }
64-
65- public bool ExecuteCommand ( FindReferencesCommandArgs args , CommandExecutionContext context )
66- {
67- var subjectBuffer = args . SubjectBuffer ;
68-
69- // Get the selection that user has in our buffer (this also works if there
70- // is no selection and the caret is just at a single position). If we
71- // can't get the selection, or there are multiple spans for it (i.e. a
72- // box selection), then don't do anything.
73- var snapshotSpans = args . TextView . Selection . GetSnapshotSpansOnBuffer ( subjectBuffer ) ;
74- if ( snapshotSpans . Count == 1 )
75- {
76- var selectedSpan = snapshotSpans [ 0 ] ;
77- var ( document , service ) = GetDocumentAndService ( subjectBuffer . CurrentSnapshot ) ;
78- if ( document != null )
79- {
80- // Do a find-refs at the *start* of the selection. That way if the
81- // user has selected a symbol that has another symbol touching it
82- // on the right (i.e. Goo++ ), then we'll do the find-refs on the
83- // symbol selected, not the symbol following.
84- if ( TryExecuteCommand ( selectedSpan . Start , document , service ) )
85- {
86- return true ;
87- }
88- }
89- }
90-
91- return false ;
92- }
38+ public override string DisplayName => EditorFeaturesResources . Find_References ;
9339
94- private static ( Document , IFindUsagesService ) GetDocumentAndService ( ITextSnapshot snapshot )
95- {
96- var document = snapshot . GetOpenDocumentInCurrentContextWithChanges ( ) ;
97- return ( document , document ? . GetLanguageService < IFindUsagesService > ( ) ) ;
98- }
99-
100- private bool TryExecuteCommand ( int caretPosition , Document document , IFindUsagesService findUsagesService )
101- {
102- // See if we're running on a host that can provide streaming results.
103- // We'll both need a FAR service that can stream results to us, and
104- // a presenter that can accept streamed results.
105- if ( findUsagesService != null && _streamingPresenter != null )
106- {
107- // kick this work off in a fire and forget fashion. Importantly, this means we do
108- // not pass in any ambient cancellation information as the execution of this command
109- // will complete and will have no bearing on the computation of the references we compute.
110- _ = StreamingFindReferencesAsync ( document , caretPosition , findUsagesService , _streamingPresenter ) ;
111- return true ;
112- }
40+ protected override FunctionId FunctionId => FunctionId . CommandHandler_FindAllReference ;
11341
114- return false ;
115- }
42+ /// <summary>
43+ /// For find-refs, we *always* use the window. Even if there is only a single result. This is not a 'go' command
44+ /// which imperatively tries to navigate to the location if possible. The intent here is to keep the results in view
45+ /// so that the user can always refer to them, even as they do other work.
46+ /// </summary>
47+ protected override bool NavigateToSingleResultIfQuick => false ;
11648
117- private async Task StreamingFindReferencesAsync (
118- Document document ,
119- int caretPosition ,
120- IFindUsagesService findUsagesService ,
121- IStreamingFindUsagesPresenter presenter )
122- {
123- try
49+ protected override StreamingFindUsagesPresenterOptions GetStreamingPresenterOptions ( Document document )
50+ => new ( )
12451 {
125- using var token = _asyncListener . BeginAsyncOperation ( nameof ( StreamingFindReferencesAsync ) ) ;
126- var classificationOptions = _globalOptions . GetClassificationOptionsProvider ( ) ;
52+ SupportsReferences = true ,
53+ IncludeContainingTypeAndMemberColumns = document . Project . SupportsCompilation ,
54+ IncludeKindColumn = document . Project . Language != LanguageNames . FSharp
55+ } ;
12756
128- // Let the presented know we're starting a search. It will give us back the context object that the FAR
129- // service will push results into. This operation is not externally cancellable. Instead, the find refs
130- // window will cancel it if another request is made to use it.
131- var ( context , cancellationToken ) = presenter . StartSearch (
132- EditorFeaturesResources . Find_References ,
133- new StreamingFindUsagesPresenterOptions ( )
134- {
135- SupportsReferences = true ,
136- IncludeContainingTypeAndMemberColumns = document . Project . SupportsCompilation ,
137- IncludeKindColumn = document . Project . Language != LanguageNames . FSharp
138- } ) ;
139-
140- using ( Logger . LogBlock (
141- FunctionId . CommandHandler_FindAllReference ,
142- KeyValueLogMessage . Create ( LogType . UserAction , static m => m [ "type" ] = "streaming" ) ,
143- cancellationToken ) )
144- {
145- try
146- {
147- await findUsagesService . FindReferencesAsync ( context , document , caretPosition , classificationOptions , cancellationToken ) . ConfigureAwait ( false ) ;
148- }
149- finally
150- {
151- await context . OnCompletedAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
152- }
153- }
154- }
155- catch ( OperationCanceledException )
156- {
157- }
158- catch ( Exception e ) when ( FatalError . ReportAndCatch ( e ) )
159- {
160- }
161- }
162- #endif
57+ protected override Task FindActionAsync ( IFindUsagesContext context , Document document , IFindUsagesService service , int caretPosition , CancellationToken cancellationToken )
58+ => service . FindReferencesAsync ( context , document , caretPosition , this . ClassificationOptionsProvider , cancellationToken ) ;
16359}
0 commit comments