1414using Microsoft . CodeAnalysis . Editor . Shared . Extensions ;
1515using Microsoft . CodeAnalysis . Editor . Shared . Utilities ;
1616using Microsoft . CodeAnalysis . Host . Mef ;
17+ using Microsoft . CodeAnalysis . Options ;
1718using Microsoft . CodeAnalysis . Shared . TestHooks ;
1819using Microsoft . CodeAnalysis . Text ;
1920using Microsoft . CodeAnalysis . Threading ;
@@ -29,27 +30,31 @@ namespace Microsoft.CodeAnalysis.Copilot;
2930[ TextViewRole ( PredefinedTextViewRoles . Document ) ]
3031internal sealed class CopilotWpfTextViewCreationListener : IWpfTextViewCreationListener
3132{
33+ private readonly IGlobalOptionService _globalOptions ;
3234 private readonly IThreadingContext _threadingContext ;
3335 private readonly Lazy < SuggestionServiceBase > _suggestionServiceBase ;
3436 private readonly IAsynchronousOperationListener _listener ;
3537
36- private readonly AsyncBatchingWorkQueue < SuggestionAcceptedEventArgs > _workQueue ;
38+ private readonly AsyncBatchingWorkQueue < ( bool accepted , ProposalBase proposal ) > _completionWorkQueue ;
3739
3840 private int _started ;
3941
4042 [ ImportingConstructor ]
4143 [ Obsolete ( MefConstruction . ImportingConstructorMessage , error : true ) ]
4244 public CopilotWpfTextViewCreationListener (
45+ IGlobalOptionService globalOptions ,
4346 IThreadingContext threadingContext ,
4447 Lazy < SuggestionServiceBase > suggestionServiceBase ,
4548 IAsynchronousOperationListenerProvider listenerProvider )
4649 {
50+ _globalOptions = globalOptions ;
4751 _threadingContext = threadingContext ;
4852 _suggestionServiceBase = suggestionServiceBase ;
4953 _listener = listenerProvider . GetListener ( FeatureAttribute . CopilotChangeAnalysis ) ;
50- _workQueue = new AsyncBatchingWorkQueue < SuggestionAcceptedEventArgs > (
54+
55+ _completionWorkQueue = new AsyncBatchingWorkQueue < ( bool accepted , ProposalBase proposal ) > (
5156 DelayTimeSpan . Idle ,
52- ProcessEventsAsync ,
57+ ProcessCompletionEventsAsync ,
5358 _listener ,
5459 _threadingContext . DisposalToken ) ;
5560 }
@@ -64,30 +69,41 @@ public void TextViewCreated(IWpfTextView textView)
6469 Task . Run ( ( ) =>
6570 {
6671 var suggestionService = _suggestionServiceBase . Value ;
67- suggestionService . SuggestionAccepted += OnSuggestionAccepted ;
72+ suggestionService . SuggestionAccepted += OnCompletionSuggestionAccepted ;
73+ suggestionService . SuggestionDismissed += OnCompletionSuggestionDismissed ;
6874 } ) . CompletesAsyncOperation ( token ) ;
6975 }
7076 }
7177
72- private void OnSuggestionAccepted ( object sender , SuggestionAcceptedEventArgs e )
78+ private void OnCompletionSuggestionAccepted ( object sender , SuggestionAcceptedEventArgs e )
79+ => OnCompletionSuggestionEvent ( accepted : true , e . FinalProposal ) ;
80+
81+ private void OnCompletionSuggestionDismissed ( object sender , SuggestionDismissedEventArgs e )
82+ => OnCompletionSuggestionEvent ( accepted : false , e . FinalProposal ) ;
83+
84+ private void OnCompletionSuggestionEvent ( bool accepted , ProposalBase ? proposal )
7385 {
74- if ( e . FinalProposal . Edits . Count == 0 )
86+ if ( proposal is not { Edits . Count : > 0 } )
7587 return ;
7688
77- _workQueue . AddWork ( e ) ;
89+ _completionWorkQueue . AddWork ( ( accepted , proposal ) ) ;
7890 }
7991
80- private async ValueTask ProcessEventsAsync (
81- ImmutableSegmentedList < SuggestionAcceptedEventArgs > list , CancellationToken cancellationToken )
92+ private async ValueTask ProcessCompletionEventsAsync (
93+ ImmutableSegmentedList < ( bool accepted , ProposalBase proposal ) > list , CancellationToken cancellationToken )
8294 {
83- foreach ( var eventArgs in list )
84- await ProcessEventAsync ( eventArgs , cancellationToken ) . ConfigureAwait ( false ) ;
95+ // Ignore if analyzing changes is disabled for this user.
96+ if ( ! _globalOptions . GetOption ( CopilotOptions . AnalyzeCopilotChanges ) )
97+ return ;
98+
99+ foreach ( var ( accepted , proposal ) in list )
100+ await ProcessCompletionEventAsync ( accepted , proposal , cancellationToken ) . ConfigureAwait ( false ) ;
85101 }
86102
87- private static async ValueTask ProcessEventAsync (
88- SuggestionAcceptedEventArgs eventArgs , CancellationToken cancellationToken )
103+ private static async ValueTask ProcessCompletionEventAsync (
104+ bool accepted , ProposalBase proposal , CancellationToken cancellationToken )
89105 {
90- var proposal = eventArgs . FinalProposal ;
106+ const string featureId = "Completion" ;
91107 var proposalId = proposal . ProposalId ;
92108
93109 foreach ( var editGroup in proposal . Edits . GroupBy ( e => e . Span . Snapshot ) )
@@ -100,13 +116,22 @@ private static async ValueTask ProcessEventAsync(
100116 if ( document is null )
101117 continue ;
102118
119+ // Currently we do not support analyzing languges other than C# and VB. This is because we only want to do
120+ // this analsis in our OOP process to avoid perf impact on the VS process. And we don't have OOP for other
121+ // languages yet.
122+ if ( ! document . SupportsSemanticModel )
123+ continue ;
124+
103125 var normalizedEdits = Normalize ( editGroup ) ;
104126 if ( normalizedEdits . IsDefaultOrEmpty )
105127 continue ;
106128
107129 var changeAnalysisService = document . Project . Solution . Services . GetRequiredService < ICopilotChangeAnalysisService > ( ) ;
108- await changeAnalysisService . AnalyzeChangeAsync (
109- document , normalizedEdits , proposalId , cancellationToken ) . ConfigureAwait ( false ) ;
130+ var analysisResult = await changeAnalysisService . AnalyzeChangeAsync (
131+ document , normalizedEdits , cancellationToken ) . ConfigureAwait ( false ) ;
132+
133+ CopilotChangeAnalysisUtilities . LogCopilotChangeAnalysis (
134+ featureId , accepted , proposalId , analysisResult , cancellationToken ) . Dispose ( ) ;
110135 }
111136 }
112137
0 commit comments