From 92cc8ef32c2c97d2630f22abc62416da33c21be6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 11 Apr 2025 17:13:59 -0700 Subject: [PATCH 01/11] Initial stubs --- eng/targets/Services.props | 1 + .../Collections/ImmutableArrayExtensions.cs | 3 + .../Copilot/CopilotWpfTextCreationListener.cs | 128 ++++++++++++++++++ .../Portable/Copilot/CopilotChangeAnalysis.cs | 49 +++++++ .../Copilot/ICopilotChangeAnalysisService.cs | 88 ++++++++++++ .../IRemoteCopilotChangeAnalysisService.cs | 19 +++ .../Core/Portable/CodeActions/CodeAction.cs | 1 + .../Shared/TestHooks/FeatureAttribute.cs | 1 + .../Remote/InProcRemostHostClient.cs | 1 + .../Remote/Core/ServiceDescriptors.cs | 2 + .../RemoteCopilotChangeAnalysisService.cs | 40 ++++++ .../RemoteSemanticClassificationService.cs | 1 + .../TextSpanMutableIntervalTree.cs | 3 + .../Compiler/Core/Log/FunctionId.cs | 2 + 14 files changed, 339 insertions(+) create mode 100644 src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs create mode 100644 src/Features/Core/Portable/Copilot/CopilotChangeAnalysis.cs create mode 100644 src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs create mode 100644 src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs create mode 100644 src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs diff --git a/eng/targets/Services.props b/eng/targets/Services.props index 409f1b282f9a1..acf686c9e0571 100644 --- a/eng/targets/Services.props +++ b/eng/targets/Services.props @@ -16,6 +16,7 @@ + diff --git a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs index a2c7638b9b25e..b38795916cb7b 100644 --- a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs +++ b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs @@ -1295,6 +1295,9 @@ internal static bool SequenceEqual(this ImmutableArray internal static int IndexOf(this ImmutableArray array, T item, IEqualityComparer comparer) => array.IndexOf(item, startIndex: 0, comparer); + internal static bool IsSorted(this ImmutableArray array, Comparison comparison) + => IsSorted(array, Comparer.Create(comparison)); + internal static bool IsSorted(this ImmutableArray array, IComparer? comparer = null) { comparer ??= Comparer.Default; diff --git a/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs b/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs new file mode 100644 index 0000000000000..56a2ca4f4b68a --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs @@ -0,0 +1,128 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.ComponentModel.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; +using Microsoft.VisualStudio.Language.Proposals; +using Microsoft.VisualStudio.Language.Suggestions; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.CodeAnalysis.Copilot; + +[Export(typeof(IWpfTextViewCreationListener))] +[ContentType(ContentTypeNames.RoslynContentType)] +[TextViewRole(PredefinedTextViewRoles.Document)] +internal sealed class CopilotWpfTextViewCreationListener : IWpfTextViewCreationListener +{ + private readonly IThreadingContext _threadingContext; + private readonly Lazy _suggestionServiceBase; + + private readonly AsyncBatchingWorkQueue _workQueue; + + private int _started; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CopilotWpfTextViewCreationListener( + IThreadingContext threadingContext, + Lazy suggestionServiceBase, + IAsynchronousOperationListenerProvider listenerProvider) + { + _threadingContext = threadingContext; + _suggestionServiceBase = suggestionServiceBase; + _workQueue = new AsyncBatchingWorkQueue( + DelayTimeSpan.Idle, + ProcessEventsAsync, + listenerProvider.GetListener(FeatureAttribute.CopilotChangeAnalysis), + _threadingContext.DisposalToken); + } + + public void TextViewCreated(IWpfTextView textView) + { + // On the first roslyn text view created, kick off work to hydrate the suggestion service and register to events + // from it. + if (Interlocked.CompareExchange(ref _started, 1, 0) == 0) + { + _ = Task.Run(() => + { + var suggestionService = _suggestionServiceBase.Value; + suggestionService.SuggestionAccepted += OnSuggestionAccepted; + }); + } + } + + private void OnSuggestionAccepted(object sender, SuggestionAcceptedEventArgs e) + { + if (e.FinalProposal.Edits.Count == 0) + return; + + _workQueue.AddWork(e); + } + + private async ValueTask ProcessEventsAsync( + ImmutableSegmentedList list, CancellationToken cancellationToken) + { + foreach (var eventArgs in list) + await ProcessEventAsync(eventArgs, cancellationToken).ConfigureAwait(false); + } + + private static async ValueTask ProcessEventAsync( + SuggestionAcceptedEventArgs eventArgs, CancellationToken cancellationToken) + { + foreach (var editGroup in eventArgs.FinalProposal.Edits.GroupBy(e => e.Span.Snapshot)) + { + cancellationToken.ThrowIfCancellationRequested(); + + var snapshot = editGroup.Key; + var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); + + if (document is null) + continue; + + var normalizedEdits = Normalize(editGroup); + if (normalizedEdits.IsDefaultOrEmpty) + continue; + + var changeAnalysisService = document.Project.Solution.Services.GetRequiredService(); + await changeAnalysisService.AnalyzeChangeAsync(document, normalizedEdits, cancellationToken).ConfigureAwait(false); + } + } + + private static ImmutableArray Normalize(IEnumerable editGroup) + { + using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); + foreach (var edit in editGroup) + builder.Add(new TextChange(edit.Span.Span.ToTextSpan(), edit.ReplacementText)); + + // Ensure everything is sorted. + builder.Sort(static (c1, c2) => c1.Span.Start - c2.Span.Start); + + // Now, go through and make sure no edit overlaps another. + for (int i = 1, n = builder.Count; i < n; i++) + { + var lastEdit = builder[i - 1]; + var currentEdit = builder[i]; + + if (lastEdit.Span.OverlapsWith(currentEdit.Span)) + return default; + } + + // Things look good. Can process these sorted edits. + return builder.ToImmutableAndClear(); + } +} diff --git a/src/Features/Core/Portable/Copilot/CopilotChangeAnalysis.cs b/src/Features/Core/Portable/Copilot/CopilotChangeAnalysis.cs new file mode 100644 index 0000000000000..5b963517f90cf --- /dev/null +++ b/src/Features/Core/Portable/Copilot/CopilotChangeAnalysis.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Runtime.Serialization; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Copilot; + +/// Total time to do all analysis (including diagnostics, code fixes, and application). +/// Total time to do all diagnostic computation over all diagnostic kinds. +[DataContract] +internal readonly record struct CopilotChangeAnalysis( + [property: DataMember(Order = 0)] bool Succeeded, + [property: DataMember(Order = 1)] TimeSpan TotalAnalysisTime, + [property: DataMember(Order = 2)] TimeSpan TotalDiagnosticComputationTime, + [property: DataMember(Order = 3)] ImmutableArray DiagnosticAnalyses, + [property: DataMember(Order = 4)] CopilotCodeFixAnalysis CodeFixAnalysis); + +/// What diagnostic kind this is analysis data for. +/// How long it took to produce the diagnostics for this diagnostic kind. +/// Mapping from to the number of diagnostics produced for that id. +/// Mapping from to the number of diagnostics produced for that category. +/// Mapping from to the number of diagnostics produced for that severity. +[DataContract] +internal readonly record struct CopilotDiagnosticAnalysis( + [property: DataMember(Order = 0)] DiagnosticKind Kind, + [property: DataMember(Order = 1)] TimeSpan ComputationTime, + [property: DataMember(Order = 2)] Dictionary IdToCount, + [property: DataMember(Order = 3)] Dictionary CategoryToCount, + [property: DataMember(Order = 4)] Dictionary SeverityToCount); + +/// Total time to compute code fixes for the changed regions. +/// Total time to apply code fixes for the changed regions. +/// Mapping from diagnostic id to to how many diagnostics with that id had fixes. +/// Mapping from diagnostic id to the total time taken to fix diagnostics with that id. +/// Mapping from diagnostic id to the name of the provider that provided the fix. +/// Mapping from provider name to the total time taken to fix diagnostics with that provider. +[DataContract] +internal readonly record struct CopilotCodeFixAnalysis( + [property: DataMember(Order = 0)] TimeSpan TotalComputationTime, + [property: DataMember(Order = 1)] TimeSpan TotalApplicationTime, + [property: DataMember(Order = 2)] Dictionary DiagnosticIdToCount, + [property: DataMember(Order = 3)] Dictionary DiagnosticIdToApplicationTime, + [property: DataMember(Order = 4)] Dictionary> DiagnosticIdToProviderName, + [property: DataMember(Order = 5)] Dictionary ProviderNameToApplicationTime); diff --git a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs new file mode 100644 index 0000000000000..a78e52f69dead --- /dev/null +++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Copilot; + +internal interface ICopilotChangeAnalysisService : IWorkspaceService +{ + /// + /// Kicks of work to analyze a change that copilot suggested making to a document. is + /// the state of the document prior to the edits, and are the changes Copilot wants to + /// make to it. must be sorted and normalized before calling this. + /// + Task AnalyzeChangeAsync(Document document, ImmutableArray changes, CancellationToken cancellationToken); +} + +[ExportWorkspaceServiceFactory(typeof(ICopilotChangeAnalysisService)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class DefaultCopilotChangeAnalysisServiceFactory( + ICodeFixService codeFixService, + IDiagnosticAnalyzerService diagnosticAnalyzerService) : IWorkspaceServiceFactory +{ + public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) + => new DefaultCopilotChangeAnalysisService(codeFixService, diagnosticAnalyzerService, workspaceServices); + + private sealed class DefaultCopilotChangeAnalysisService( + ICodeFixService codeFixService, + IDiagnosticAnalyzerService diagnosticAnalyzerService, + HostWorkspaceServices workspaceServices) : ICopilotChangeAnalysisService + { + private readonly ICodeFixService _codeFixService = codeFixService; + private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService = diagnosticAnalyzerService; + private readonly HostWorkspaceServices _workspaceServices = workspaceServices; + + public async Task AnalyzeChangeAsync( + Document document, + ImmutableArray changes, + CancellationToken cancellationToken) + { + if (!document.SupportsSemanticModel) + return default; + + Contract.ThrowIfTrue(!changes.IsSorted(static (c1, c2) => c1.Span.Start - c2.Span.Start), "'changes' was not sorted."); + Contract.ThrowIfTrue(new NormalizedTextSpanCollection(changes.Select(c => c.Span)).Count != changes.Length, "'changes' was not normalized."); + Contract.ThrowIfTrue(document.Project.Solution.Workspace != _workspaceServices.Workspace); + + var client = await RemoteHostClient.TryGetClientAsync( + _workspaceServices.Workspace, cancellationToken).ConfigureAwait(false); + + if (client != null) + { + var value = await client.TryInvokeAsync( + // Don't need to sync the entire solution over. Just the cone of projects this document it contained within. + document.Project, + (service, checksum, cancellationToken) => service.AnalyzeChangeAsync(checksum, document.Id, changes, cancellationToken), + cancellationToken).ConfigureAwait(false); + return value.HasValue ? value.Value : default; + } + else + { + return await AnalyzeChangeInCurrentProcessAsync(document, changes, cancellationToken).ConfigureAwait(false); + } + } + + private async Task AnalyzeChangeInCurrentProcessAsync( + Document document, + ImmutableArray changes, + CancellationToken cancellationToken) + { + return default; + } + } +} diff --git a/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs new file mode 100644 index 0000000000000..185b9de3eb944 --- /dev/null +++ b/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Copilot; + +/// Remote version of +internal interface IRemoteCopilotChangeAnalysisService : IWorkspaceService +{ + /// + ValueTask AnalyzeChangeAsync( + Checksum solutionChecksum, DocumentId documentId, ImmutableArray edits, CancellationToken cancellationToken); +} diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs index dbd9f775ca9d0..644c85de0fd18 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs +++ b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs @@ -8,6 +8,7 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup; diff --git a/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs b/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs index 343deb91c658c..1d19cac5f4bd5 100644 --- a/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs +++ b/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs @@ -20,6 +20,7 @@ internal static class FeatureAttribute public const string CompletionSet = nameof(CompletionSet); public const string CopilotImplementNotImplementedException = nameof(CopilotImplementNotImplementedException); public const string CopilotSuggestions = nameof(CopilotSuggestions); + public const string CopilotChangeAnalysis = nameof(CopilotChangeAnalysis); public const string DesignerAttributes = nameof(DesignerAttributes); public const string DiagnosticService = nameof(DiagnosticService); public const string DocumentOutline = nameof(DocumentOutline); diff --git a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs index 6800cd6da60b2..10a4b221077c8 100644 --- a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs @@ -182,6 +182,7 @@ public InProcRemoteServices(SolutionServices workspaceServices, TraceListener? t RegisterRemoteBrokeredService(new RemoteAsynchronousOperationListenerService.Factory()); RegisterRemoteBrokeredService(new RemoteCodeLensReferencesService.Factory()); RegisterRemoteBrokeredService(new RemoteConvertTupleToStructCodeRefactoringService.Factory()); + RegisterRemoteBrokeredService(new RemoteCopilotChangeAnalysisService.Factory()); RegisterRemoteBrokeredService(new RemoteDependentTypeFinderService.Factory()); RegisterRemoteBrokeredService(new RemoteDesignerAttributeDiscoveryService.Factory()); RegisterRemoteBrokeredService(new RemoteDiagnosticAnalyzerService.Factory()); diff --git a/src/Workspaces/Remote/Core/ServiceDescriptors.cs b/src/Workspaces/Remote/Core/ServiceDescriptors.cs index 376fe434d2b9f..30daa9b5dcf3c 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptors.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CodeLens; using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.ConvertTupleToStruct; +using Microsoft.CodeAnalysis.Copilot; using Microsoft.CodeAnalysis.DesignerAttribute; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.DocumentHighlighting; @@ -58,6 +59,7 @@ internal sealed class ServiceDescriptors (typeof(IRemoteAsynchronousOperationListenerService), null), (typeof(IRemoteCodeLensReferencesService), null), (typeof(IRemoteConvertTupleToStructCodeRefactoringService), null), + (typeof(IRemoteCopilotChangeAnalysisService), null), (typeof(IRemoteDependentTypeFinderService), null), (typeof(IRemoteDesignerAttributeDiscoveryService), typeof(IRemoteDesignerAttributeDiscoveryService.ICallback)), (typeof(IRemoteDiagnosticAnalyzerService), null), diff --git a/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs b/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs new file mode 100644 index 0000000000000..b0e025735b9c7 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Copilot; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Remote; + +internal sealed partial class RemoteCopilotChangeAnalysisService( + in BrokeredServiceBase.ServiceConstructionArguments arguments) + : BrokeredServiceBase(arguments), IRemoteCopilotChangeAnalysisService +{ + internal sealed class Factory : FactoryBase + { + protected override IRemoteCopilotChangeAnalysisService CreateService(in ServiceConstructionArguments arguments) + => new RemoteCopilotChangeAnalysisService(arguments); + } + + public ValueTask AnalyzeChangeAsync( + Checksum solutionChecksum, + DocumentId documentId, + ImmutableArray edits, + CancellationToken cancellationToken) + { + return RunServiceAsync(solutionChecksum, async solution => + { + var document = await solution.GetRequiredDocumentAsync( + documentId, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); + + var service = solution.Services.GetRequiredService(); + await service.AnalyzeChangeAsync( + document, edits, cancellationToken).ConfigureAwait(false); + }, cancellationToken); + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs index 6053c02cd7338..d66319d166ea5 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Copilot; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/TextSpanMutableIntervalTree.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/TextSpanMutableIntervalTree.cs index 0c8647b4e755f..4907f3af8e306 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/TextSpanMutableIntervalTree.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/TextSpanMutableIntervalTree.cs @@ -26,4 +26,7 @@ public TextSpanMutableIntervalTree(params TextSpan[]? values) : this((IEnumerabl public bool HasIntervalThatIntersectsWith(TextSpan span) => this.HasIntervalThatIntersectsWith(span.Start, span.Length); + + public bool HasIntervalThatOverlapsWith(TextSpan span) + => this.HasIntervalThatOverlapsWith(span.Start, span.Length); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index 8c37a0ce21abc..b9ccc9a4b2e3a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -647,6 +647,8 @@ internal enum FunctionId Copilot_Rename = 851, + Copilot_AnalyzeChange = 860, + VSCode_LanguageServer_Started = 860, VSCode_Project_Load_Started = 861, VSCode_Projects_Load_Completed = 862, From 52c3a04798359d7aebd67d83881c0974eee25340 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Apr 2025 09:17:28 -0700 Subject: [PATCH 02/11] Async token --- .../Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs | 9 ++++++--- .../Portable/Copilot/ICopilotChangeAnalysisService.cs | 8 ++++++++ src/Workspaces/Core/Portable/CodeActions/CodeAction.cs | 1 - .../RemoteSemanticClassificationService.cs | 1 - 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs b/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs index 56a2ca4f4b68a..6fb682b26acda 100644 --- a/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs +++ b/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs @@ -31,6 +31,7 @@ internal sealed class CopilotWpfTextViewCreationListener : IWpfTextViewCreationL { private readonly IThreadingContext _threadingContext; private readonly Lazy _suggestionServiceBase; + private readonly IAsynchronousOperationListener _listener; private readonly AsyncBatchingWorkQueue _workQueue; @@ -45,10 +46,11 @@ public CopilotWpfTextViewCreationListener( { _threadingContext = threadingContext; _suggestionServiceBase = suggestionServiceBase; + _listener = listenerProvider.GetListener(FeatureAttribute.CopilotChangeAnalysis); _workQueue = new AsyncBatchingWorkQueue( DelayTimeSpan.Idle, ProcessEventsAsync, - listenerProvider.GetListener(FeatureAttribute.CopilotChangeAnalysis), + _listener, _threadingContext.DisposalToken); } @@ -58,11 +60,12 @@ public void TextViewCreated(IWpfTextView textView) // from it. if (Interlocked.CompareExchange(ref _started, 1, 0) == 0) { - _ = Task.Run(() => + var token = _listener.BeginAsyncOperation(nameof(TextViewCreated)); + Task.Run(() => { var suggestionService = _suggestionServiceBase.Value; suggestionService.SuggestionAccepted += OnSuggestionAccepted; - }); + }).CompletesAsyncOperation(token); } } diff --git a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index a78e52f69dead..df068499b07c7 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -43,8 +43,10 @@ private sealed class DefaultCopilotChangeAnalysisService( IDiagnosticAnalyzerService diagnosticAnalyzerService, HostWorkspaceServices workspaceServices) : ICopilotChangeAnalysisService { +#pragma warning disable IDE0052 // Remove unread private members private readonly ICodeFixService _codeFixService = codeFixService; private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService = diagnosticAnalyzerService; +#pragma warning restore IDE0052 // Remove unread private members private readonly HostWorkspaceServices _workspaceServices = workspaceServices; public async Task AnalyzeChangeAsync( @@ -77,6 +79,9 @@ public async Task AnalyzeChangeAsync( } } +#pragma warning disable CA1822 // Mark members as static +#pragma warning disable IDE0060 // Remove unused parameter +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously private async Task AnalyzeChangeInCurrentProcessAsync( Document document, ImmutableArray changes, @@ -84,5 +89,8 @@ private async Task AnalyzeChangeInCurrentProcessAsync( { return default; } +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously +#pragma warning restore IDE0060 // Remove unused parameter +#pragma warning restore CA1822 // Mark members as static } } diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs index 644c85de0fd18..dbd9f775ca9d0 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs +++ b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs @@ -8,7 +8,6 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup; diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs index d66319d166ea5..6053c02cd7338 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Copilot; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; From 4478ccafd6795327122f4c31f8ed4ae68be7237c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Apr 2025 21:40:23 -0700 Subject: [PATCH 03/11] Change value --- .../Compiler/Core/Log/FunctionId.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index b9ccc9a4b2e3a..2af501ba1e198 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -645,9 +645,9 @@ internal enum FunctionId Copilot_Implement_NotImplementedException_Failed = 831, Copilot_Implement_NotImplementedException_Completed = 832, - Copilot_Rename = 851, + Copilot_AnalyzeChange = 840, - Copilot_AnalyzeChange = 860, + Copilot_Rename = 851, VSCode_LanguageServer_Started = 860, VSCode_Project_Load_Started = 861, From 33583690fbad03e88c58c7847093e6e3b866049d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Apr 2025 21:41:07 -0700 Subject: [PATCH 04/11] Fix --- .../Services/Copilot/RemoteCopilotChangeAnalysisService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs b/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs index b0e025735b9c7..627a0587d070f 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs @@ -21,7 +21,7 @@ protected override IRemoteCopilotChangeAnalysisService CreateService(in ServiceC => new RemoteCopilotChangeAnalysisService(arguments); } - public ValueTask AnalyzeChangeAsync( + public ValueTask AnalyzeChangeAsync( Checksum solutionChecksum, DocumentId documentId, ImmutableArray edits, @@ -33,7 +33,7 @@ public ValueTask AnalyzeChangeAsync( documentId, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); var service = solution.Services.GetRequiredService(); - await service.AnalyzeChangeAsync( + return await service.AnalyzeChangeAsync( document, edits, cancellationToken).ConfigureAwait(false); }, cancellationToken); } From 55806276e06245aea488871f365a02d613a54502 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Apr 2025 22:58:32 -0700 Subject: [PATCH 05/11] Allow null --- .../Copilot/ICopilotChangeAnalysisService.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index df068499b07c7..1ef66c2d44376 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -32,20 +32,20 @@ internal interface ICopilotChangeAnalysisService : IWorkspaceService [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class DefaultCopilotChangeAnalysisServiceFactory( - ICodeFixService codeFixService, - IDiagnosticAnalyzerService diagnosticAnalyzerService) : IWorkspaceServiceFactory + [Import(AllowDefault = true)] ICodeFixService? codeFixService, + [Import(AllowDefault = true)] IDiagnosticAnalyzerService? diagnosticAnalyzerService) : IWorkspaceServiceFactory { public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) => new DefaultCopilotChangeAnalysisService(codeFixService, diagnosticAnalyzerService, workspaceServices); private sealed class DefaultCopilotChangeAnalysisService( - ICodeFixService codeFixService, - IDiagnosticAnalyzerService diagnosticAnalyzerService, + ICodeFixService? codeFixService, + IDiagnosticAnalyzerService? diagnosticAnalyzerService, HostWorkspaceServices workspaceServices) : ICopilotChangeAnalysisService { #pragma warning disable IDE0052 // Remove unread private members - private readonly ICodeFixService _codeFixService = codeFixService; - private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService = diagnosticAnalyzerService; + private readonly ICodeFixService? _codeFixService = codeFixService; + private readonly IDiagnosticAnalyzerService? _diagnosticAnalyzerService = diagnosticAnalyzerService; #pragma warning restore IDE0052 // Remove unread private members private readonly HostWorkspaceServices _workspaceServices = workspaceServices; @@ -54,7 +54,7 @@ public async Task AnalyzeChangeAsync( ImmutableArray changes, CancellationToken cancellationToken) { - if (!document.SupportsSemanticModel) + if (_codeFixService is null || _diagnosticAnalyzerService is null || !document.SupportsSemanticModel) return default; Contract.ThrowIfTrue(!changes.IsSorted(static (c1, c2) => c1.Span.Start - c2.Span.Start), "'changes' was not sorted."); From f596a128351e5ee2eb249893c14247b849cea1dd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Apr 2025 23:15:47 -0700 Subject: [PATCH 06/11] Simplify --- .../Core/Portable/Copilot/ICopilotChangeAnalysisService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index 1ef66c2d44376..250a76715119f 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -54,7 +54,7 @@ public async Task AnalyzeChangeAsync( ImmutableArray changes, CancellationToken cancellationToken) { - if (_codeFixService is null || _diagnosticAnalyzerService is null || !document.SupportsSemanticModel) + if (!document.SupportsSemanticModel) return default; Contract.ThrowIfTrue(!changes.IsSorted(static (c1, c2) => c1.Span.Start - c2.Span.Start), "'changes' was not sorted."); From a0191e80ca5aa811e04ff64307a980de1331b686 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Apr 2025 23:17:33 -0700 Subject: [PATCH 07/11] Simplufy --- .../Copilot/ICopilotChangeAnalysisService.cs | 82 ++++++++----------- 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index 250a76715119f..885791b9e0120 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -28,69 +28,57 @@ internal interface ICopilotChangeAnalysisService : IWorkspaceService Task AnalyzeChangeAsync(Document document, ImmutableArray changes, CancellationToken cancellationToken); } -[ExportWorkspaceServiceFactory(typeof(ICopilotChangeAnalysisService)), Shared] +[ExportWorkspaceService(typeof(ICopilotChangeAnalysisService)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class DefaultCopilotChangeAnalysisServiceFactory( +internal sealed class DefaultCopilotChangeAnalysisService( [Import(AllowDefault = true)] ICodeFixService? codeFixService, - [Import(AllowDefault = true)] IDiagnosticAnalyzerService? diagnosticAnalyzerService) : IWorkspaceServiceFactory + [Import(AllowDefault = true)] IDiagnosticAnalyzerService? diagnosticAnalyzerService) : ICopilotChangeAnalysisService { - public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new DefaultCopilotChangeAnalysisService(codeFixService, diagnosticAnalyzerService, workspaceServices); - - private sealed class DefaultCopilotChangeAnalysisService( - ICodeFixService? codeFixService, - IDiagnosticAnalyzerService? diagnosticAnalyzerService, - HostWorkspaceServices workspaceServices) : ICopilotChangeAnalysisService - { #pragma warning disable IDE0052 // Remove unread private members - private readonly ICodeFixService? _codeFixService = codeFixService; - private readonly IDiagnosticAnalyzerService? _diagnosticAnalyzerService = diagnosticAnalyzerService; + private readonly ICodeFixService? _codeFixService = codeFixService; + private readonly IDiagnosticAnalyzerService? _diagnosticAnalyzerService = diagnosticAnalyzerService; #pragma warning restore IDE0052 // Remove unread private members - private readonly HostWorkspaceServices _workspaceServices = workspaceServices; - public async Task AnalyzeChangeAsync( - Document document, - ImmutableArray changes, - CancellationToken cancellationToken) - { - if (!document.SupportsSemanticModel) - return default; + public async Task AnalyzeChangeAsync( + Document document, + ImmutableArray changes, + CancellationToken cancellationToken) + { + if (!document.SupportsSemanticModel) + return default; - Contract.ThrowIfTrue(!changes.IsSorted(static (c1, c2) => c1.Span.Start - c2.Span.Start), "'changes' was not sorted."); - Contract.ThrowIfTrue(new NormalizedTextSpanCollection(changes.Select(c => c.Span)).Count != changes.Length, "'changes' was not normalized."); - Contract.ThrowIfTrue(document.Project.Solution.Workspace != _workspaceServices.Workspace); + Contract.ThrowIfTrue(!changes.IsSorted(static (c1, c2) => c1.Span.Start - c2.Span.Start), "'changes' was not sorted."); + Contract.ThrowIfTrue(new NormalizedTextSpanCollection(changes.Select(c => c.Span)).Count != changes.Length, "'changes' was not normalized."); - var client = await RemoteHostClient.TryGetClientAsync( - _workspaceServices.Workspace, cancellationToken).ConfigureAwait(false); + var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false); - if (client != null) - { - var value = await client.TryInvokeAsync( - // Don't need to sync the entire solution over. Just the cone of projects this document it contained within. - document.Project, - (service, checksum, cancellationToken) => service.AnalyzeChangeAsync(checksum, document.Id, changes, cancellationToken), - cancellationToken).ConfigureAwait(false); - return value.HasValue ? value.Value : default; - } - else - { - return await AnalyzeChangeInCurrentProcessAsync(document, changes, cancellationToken).ConfigureAwait(false); - } + if (client != null) + { + var value = await client.TryInvokeAsync( + // Don't need to sync the entire solution over. Just the cone of projects this document it contained within. + document.Project, + (service, checksum, cancellationToken) => service.AnalyzeChangeAsync(checksum, document.Id, changes, cancellationToken), + cancellationToken).ConfigureAwait(false); + return value.HasValue ? value.Value : default; + } + else + { + return await AnalyzeChangeInCurrentProcessAsync(document, changes, cancellationToken).ConfigureAwait(false); } + } #pragma warning disable CA1822 // Mark members as static #pragma warning disable IDE0060 // Remove unused parameter #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - private async Task AnalyzeChangeInCurrentProcessAsync( - Document document, - ImmutableArray changes, - CancellationToken cancellationToken) - { - return default; - } + private async Task AnalyzeChangeInCurrentProcessAsync( + Document document, + ImmutableArray changes, + CancellationToken cancellationToken) + { + return default; + } #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously #pragma warning restore IDE0060 // Remove unused parameter #pragma warning restore CA1822 // Mark members as static - } } From ae531dd7c2a9d1cdbdad2af59ec885fd2e10fa09 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Apr 2025 23:22:21 -0700 Subject: [PATCH 08/11] Add null --- .../Core/Portable/Copilot/ICopilotChangeAnalysisService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index 885791b9e0120..e7ec1a09dc30c 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -32,8 +32,8 @@ internal interface ICopilotChangeAnalysisService : IWorkspaceService [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class DefaultCopilotChangeAnalysisService( - [Import(AllowDefault = true)] ICodeFixService? codeFixService, - [Import(AllowDefault = true)] IDiagnosticAnalyzerService? diagnosticAnalyzerService) : ICopilotChangeAnalysisService + [Import(AllowDefault = true)] ICodeFixService? codeFixService = null, + [Import(AllowDefault = true)] IDiagnosticAnalyzerService? diagnosticAnalyzerService = null) : ICopilotChangeAnalysisService { #pragma warning disable IDE0052 // Remove unread private members private readonly ICodeFixService? _codeFixService = codeFixService; From f2ab1ae682fdafbded5d2c3c9ebe4034ac884ec5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 17 Apr 2025 12:40:14 -0700 Subject: [PATCH 09/11] Add correlation id --- .../Core/Portable/Copilot/ICopilotChangeAnalysisService.cs | 6 ++++-- .../Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs | 3 ++- .../Services/Copilot/RemoteCopilotChangeAnalysisService.cs | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index e7ec1a09dc30c..ac5acfe4648d3 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -25,7 +25,7 @@ internal interface ICopilotChangeAnalysisService : IWorkspaceService /// the state of the document prior to the edits, and are the changes Copilot wants to /// make to it. must be sorted and normalized before calling this. /// - Task AnalyzeChangeAsync(Document document, ImmutableArray changes, CancellationToken cancellationToken); + Task AnalyzeChangeAsync(Document document, ImmutableArray changes, Guid correlationId, CancellationToken cancellationToken); } [ExportWorkspaceService(typeof(ICopilotChangeAnalysisService)), Shared] @@ -43,6 +43,7 @@ internal sealed class DefaultCopilotChangeAnalysisService( public async Task AnalyzeChangeAsync( Document document, ImmutableArray changes, + Guid correlationId, CancellationToken cancellationToken) { if (!document.SupportsSemanticModel) @@ -58,7 +59,8 @@ public async Task AnalyzeChangeAsync( var value = await client.TryInvokeAsync( // Don't need to sync the entire solution over. Just the cone of projects this document it contained within. document.Project, - (service, checksum, cancellationToken) => service.AnalyzeChangeAsync(checksum, document.Id, changes, cancellationToken), + (service, checksum, cancellationToken) => service.AnalyzeChangeAsync( + checksum, document.Id, changes, correlationId, cancellationToken), cancellationToken).ConfigureAwait(false); return value.HasValue ? value.Value : default; } diff --git a/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs index 185b9de3eb944..7034e906631f5 100644 --- a/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -15,5 +16,5 @@ internal interface IRemoteCopilotChangeAnalysisService : IWorkspaceService { /// ValueTask AnalyzeChangeAsync( - Checksum solutionChecksum, DocumentId documentId, ImmutableArray edits, CancellationToken cancellationToken); + Checksum solutionChecksum, DocumentId documentId, ImmutableArray edits, Guid correlationId, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs b/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs index 627a0587d070f..a45cabc05ec3d 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -25,6 +26,7 @@ public ValueTask AnalyzeChangeAsync( Checksum solutionChecksum, DocumentId documentId, ImmutableArray edits, + Guid correlationId, CancellationToken cancellationToken) { return RunServiceAsync(solutionChecksum, async solution => @@ -34,7 +36,7 @@ public ValueTask AnalyzeChangeAsync( var service = solution.Services.GetRequiredService(); return await service.AnalyzeChangeAsync( - document, edits, cancellationToken).ConfigureAwait(false); + document, edits, correlationId, cancellationToken).ConfigureAwait(false); }, cancellationToken); } } From b01653773560584babd1c6f441120001c288b5a5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 21 Apr 2025 13:51:11 -0700 Subject: [PATCH 10/11] Move to string --- .../Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs | 8 ++++++-- .../Portable/Copilot/ICopilotChangeAnalysisService.cs | 7 ++++--- .../Copilot/IRemoteCopilotChangeAnalysisService.cs | 2 +- .../Copilot/RemoteCopilotChangeAnalysisService.cs | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs b/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs index 6fb682b26acda..36da95ece96a7 100644 --- a/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs +++ b/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs @@ -87,7 +87,10 @@ private async ValueTask ProcessEventsAsync( private static async ValueTask ProcessEventAsync( SuggestionAcceptedEventArgs eventArgs, CancellationToken cancellationToken) { - foreach (var editGroup in eventArgs.FinalProposal.Edits.GroupBy(e => e.Span.Snapshot)) + var proposal = eventArgs.FinalProposal; + var proposalId = proposal.ProposalId; + + foreach (var editGroup in proposal.Edits.GroupBy(e => e.Span.Snapshot)) { cancellationToken.ThrowIfCancellationRequested(); @@ -102,7 +105,8 @@ private static async ValueTask ProcessEventAsync( continue; var changeAnalysisService = document.Project.Solution.Services.GetRequiredService(); - await changeAnalysisService.AnalyzeChangeAsync(document, normalizedEdits, cancellationToken).ConfigureAwait(false); + await changeAnalysisService.AnalyzeChangeAsync( + document, normalizedEdits, proposalId, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index ac5acfe4648d3..fd2849e1dd6d1 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -25,7 +25,8 @@ internal interface ICopilotChangeAnalysisService : IWorkspaceService /// the state of the document prior to the edits, and are the changes Copilot wants to /// make to it. must be sorted and normalized before calling this. /// - Task AnalyzeChangeAsync(Document document, ImmutableArray changes, Guid correlationId, CancellationToken cancellationToken); + Task AnalyzeChangeAsync( + Document document, ImmutableArray changes, string proposalId, CancellationToken cancellationToken); } [ExportWorkspaceService(typeof(ICopilotChangeAnalysisService)), Shared] @@ -43,7 +44,7 @@ internal sealed class DefaultCopilotChangeAnalysisService( public async Task AnalyzeChangeAsync( Document document, ImmutableArray changes, - Guid correlationId, + string proposalId, CancellationToken cancellationToken) { if (!document.SupportsSemanticModel) @@ -60,7 +61,7 @@ public async Task AnalyzeChangeAsync( // Don't need to sync the entire solution over. Just the cone of projects this document it contained within. document.Project, (service, checksum, cancellationToken) => service.AnalyzeChangeAsync( - checksum, document.Id, changes, correlationId, cancellationToken), + checksum, document.Id, changes, proposalId, cancellationToken), cancellationToken).ConfigureAwait(false); return value.HasValue ? value.Value : default; } diff --git a/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs index 7034e906631f5..fbb6347b4962b 100644 --- a/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs @@ -16,5 +16,5 @@ internal interface IRemoteCopilotChangeAnalysisService : IWorkspaceService { /// ValueTask AnalyzeChangeAsync( - Checksum solutionChecksum, DocumentId documentId, ImmutableArray edits, Guid correlationId, CancellationToken cancellationToken); + Checksum solutionChecksum, DocumentId documentId, ImmutableArray edits, string proposalId, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs b/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs index a45cabc05ec3d..a9e89cbdf36cb 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs @@ -26,7 +26,7 @@ public ValueTask AnalyzeChangeAsync( Checksum solutionChecksum, DocumentId documentId, ImmutableArray edits, - Guid correlationId, + string proposalId, CancellationToken cancellationToken) { return RunServiceAsync(solutionChecksum, async solution => @@ -36,7 +36,7 @@ public ValueTask AnalyzeChangeAsync( var service = solution.Services.GetRequiredService(); return await service.AnalyzeChangeAsync( - document, edits, correlationId, cancellationToken).ConfigureAwait(false); + document, edits, proposalId, cancellationToken).ConfigureAwait(false); }, cancellationToken); } } From 612252bcc37df8b5ed07ebe2cc2e5aa3def46850 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 24 Apr 2025 00:24:13 -0700 Subject: [PATCH 11/11] Add resource string --- src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx | 3 +++ .../Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.de.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.es.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.it.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf | 5 +++++ 14 files changed, 68 insertions(+) diff --git a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx index 4aef0ad539f80..53e69a6905331 100644 --- a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx +++ b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx @@ -240,4 +240,7 @@ Initialization + + Copilot change analysis + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf index e0cf716580e3b..32f2ea9bb8029 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf @@ -27,6 +27,11 @@ Převést řazenou kolekci členů na refaktoring struktury + + Copilot change analysis + Copilot change analysis + + Dependent type finder Vyhledávání závislých typů diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf index c61e7c1749e13..517d09c349fde 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf @@ -27,6 +27,11 @@ Tupel in Strukturrefactoring konvertieren + + Copilot change analysis + Copilot change analysis + + Dependent type finder Suche nach abhängigen Typen diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf index a5cc2d538350a..1cf88210f30f2 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf @@ -27,6 +27,11 @@ Convertir tupla en refactorización de estructura + + Copilot change analysis + Copilot change analysis + + Dependent type finder Localizador de tipos dependientes diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf index f989563604567..b87acd870b713 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf @@ -27,6 +27,11 @@ Refactorisation de la conversion de tuple en struct + + Copilot change analysis + Copilot change analysis + + Dependent type finder Outil de recherche de type dépendant diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf index 149c0bb4324a9..598f232526d7b 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf @@ -27,6 +27,11 @@ Conversione della tupla nel refactoring dello struct + + Copilot change analysis + Copilot change analysis + + Dependent type finder Strumento di ricerca tipi dipendenti diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf index 282462e1a440c..1b97dcb0b6077 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf @@ -27,6 +27,11 @@ タプルを構造体のリファクタリングに変換する + + Copilot change analysis + Copilot change analysis + + Dependent type finder 依存型の検索 diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf index 85c6ac95dfffc..e16d2b7096112 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf @@ -27,6 +27,11 @@ 튜플을 구조체 리팩터링으로 변환 + + Copilot change analysis + Copilot change analysis + + Dependent type finder 종속 형식 찾기 diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf index 3e34ecd0bb45b..728fe011d135b 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf @@ -27,6 +27,11 @@ Refaktoryzacja konwersji krotki na strukturę + + Copilot change analysis + Copilot change analysis + + Dependent type finder Wyszukiwanie typów zależnych diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf index d13db956d4483..60e3b53b5ee50 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf @@ -27,6 +27,11 @@ Converter a tupla em refatoração de struct + + Copilot change analysis + Copilot change analysis + + Dependent type finder Localizador de tipo dependente diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf index 7ca04d5b162a3..e0418e58000df 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf @@ -27,6 +27,11 @@ Преобразовать кортеж в рефакторинг структуры + + Copilot change analysis + Copilot change analysis + + Dependent type finder Поиск зависимых типов diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf index c9089a3fa2f88..5b5b22d83fe3f 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf @@ -27,6 +27,11 @@ Demeti yapıya dönüştürme yeniden düzenlemesi + + Copilot change analysis + Copilot change analysis + + Dependent type finder Bağımlı tür bulucu diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf index 290651aa5bd14..6f6fc898759df 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf @@ -27,6 +27,11 @@ 将元组转换为结构重构 + + Copilot change analysis + Copilot change analysis + + Dependent type finder 依赖类型查找器 diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf index ead886b90f311..b33950769abd0 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf @@ -27,6 +27,11 @@ 將元組轉換為結構重構 + + Copilot change analysis + Copilot change analysis + + Dependent type finder 相依類型尋找工具