diff --git a/eng/targets/Services.props b/eng/targets/Services.props
index c9b59f0c095bf..2efb19403388c 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 23e336b5d331e..ccc26beffe588 100644
--- a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs
+++ b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs
@@ -1296,6 +1296,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..36da95ece96a7
--- /dev/null
+++ b/src/EditorFeatures/Core.Wpf/Copilot/CopilotWpfTextCreationListener.cs
@@ -0,0 +1,135 @@
+// 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 IAsynchronousOperationListener _listener;
+
+ 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;
+ _listener = listenerProvider.GetListener(FeatureAttribute.CopilotChangeAnalysis);
+ _workQueue = new AsyncBatchingWorkQueue(
+ DelayTimeSpan.Idle,
+ ProcessEventsAsync,
+ _listener,
+ _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)
+ {
+ var token = _listener.BeginAsyncOperation(nameof(TextViewCreated));
+ Task.Run(() =>
+ {
+ var suggestionService = _suggestionServiceBase.Value;
+ suggestionService.SuggestionAccepted += OnSuggestionAccepted;
+ }).CompletesAsyncOperation(token);
+ }
+ }
+
+ 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)
+ {
+ var proposal = eventArgs.FinalProposal;
+ var proposalId = proposal.ProposalId;
+
+ foreach (var editGroup in proposal.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, proposalId, 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..fd2849e1dd6d1
--- /dev/null
+++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs
@@ -0,0 +1,87 @@
+// 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, string proposalId, CancellationToken cancellationToken);
+}
+
+[ExportWorkspaceService(typeof(ICopilotChangeAnalysisService)), Shared]
+[method: ImportingConstructor]
+[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+internal sealed class DefaultCopilotChangeAnalysisService(
+ [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;
+ private readonly IDiagnosticAnalyzerService? _diagnosticAnalyzerService = diagnosticAnalyzerService;
+#pragma warning restore IDE0052 // Remove unread private members
+
+ public async Task AnalyzeChangeAsync(
+ Document document,
+ ImmutableArray changes,
+ string proposalId,
+ 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.");
+
+ 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, proposalId, 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;
+ }
+#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/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs
new file mode 100644
index 0000000000000..fbb6347b4962b
--- /dev/null
+++ b/src/Features/Core/Portable/Copilot/IRemoteCopilotChangeAnalysisService.cs
@@ -0,0 +1,20 @@
+// 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.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, string proposalId, CancellationToken cancellationToken);
+}
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 60abbcc98f941..696b7d16f7b41 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/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/ServiceDescriptors.cs b/src/Workspaces/Remote/Core/ServiceDescriptors.cs
index b2e113683a662..2c88b5dee5bf4 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/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
相依類型尋找工具
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..a9e89cbdf36cb
--- /dev/null
+++ b/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotChangeAnalysisService.cs
@@ -0,0 +1,42 @@
+// 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.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,
+ string proposalId,
+ CancellationToken cancellationToken)
+ {
+ return RunServiceAsync(solutionChecksum, async solution =>
+ {
+ var document = await solution.GetRequiredDocumentAsync(
+ documentId, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false);
+
+ var service = solution.Services.GetRequiredService();
+ return await service.AnalyzeChangeAsync(
+ document, edits, proposalId, cancellationToken).ConfigureAwait(false);
+ }, cancellationToken);
+ }
+}
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 1430b8477c9dc..b0264e8e72673 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs
@@ -645,6 +645,8 @@ internal enum FunctionId
Copilot_Implement_NotImplementedException_Failed = 831,
Copilot_Implement_NotImplementedException_Completed = 832,
+ Copilot_AnalyzeChange = 840,
+
Copilot_Rename = 851,
VSCode_LanguageServer_Started = 860,