Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
38f2aed
Backport copilot text change analysis to 17.14
CyrusNajmabadi May 7, 2025
5843b90
Move ICodeFixService back to Features layer (#78132)
CyrusNajmabadi Apr 13, 2025
3426239
Fixup
CyrusNajmabadi May 7, 2025
6b3e3b6
Move IsSorted back
CyrusNajmabadi May 7, 2025
7cc45ad
FSN
CyrusNajmabadi May 7, 2025
7d2c2c7
Move DiagnosticService impl down to Features layer (#78155)
CyrusNajmabadi Apr 22, 2025
b12bd0b
fsn
CyrusNajmabadi May 7, 2025
be777bc
Make IDiagnosticAnalyzerService into an IWorkspaceService (#78359)
CyrusNajmabadi Apr 28, 2025
f177edb
revert
CyrusNajmabadi May 7, 2025
ddc025f
revert
CyrusNajmabadi May 7, 2025
4d9ce78
Fix tests
CyrusNajmabadi May 7, 2025
a2bab9d
Fix
CyrusNajmabadi May 8, 2025
4624fc1
Fix
CyrusNajmabadi May 8, 2025
2e812df
Revert
CyrusNajmabadi May 9, 2025
7c931df
Revert
CyrusNajmabadi May 9, 2025
c4e3cbb
Ensure move shows up
CyrusNajmabadi May 9, 2025
9c25643
Ensure move shows up
CyrusNajmabadi May 9, 2025
bd18a43
Revert
CyrusNajmabadi May 9, 2025
ee55894
REvert
CyrusNajmabadi May 9, 2025
10e101b
revert
CyrusNajmabadi May 9, 2025
e440f2f
Fix test
CyrusNajmabadi May 10, 2025
1dd9052
Merge branch 'release/dev17.14' into backportCopilotAnalysis
CyrusNajmabadi May 10, 2025
bf00484
Extract helper to make it easier for other features to request copilo…
CyrusNajmabadi May 9, 2025
9e04136
Collect data about which code fixes end up making changes that confli…
CyrusNajmabadi May 9, 2025
66bf8d3
Disable by default for dev17
CyrusNajmabadi May 13, 2025
601d4d3
Merge branch 'release/dev17.14' into backportCopilotAnalysis
CyrusNajmabadi May 19, 2025
258ec95
Merge branch 'release/dev17.14' into backportCopilotAnalysis
CyrusNajmabadi Jun 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/targets/Services.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.CodeLensReferences" ClassName="Microsoft.CodeAnalysis.Remote.RemoteCodeLensReferencesService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.CompilationAvailable" ClassName="Microsoft.CodeAnalysis.Remote.RemoteCompilationAvailableService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ConvertTupleToStructCodeRefactoring" ClassName="Microsoft.CodeAnalysis.Remote.RemoteConvertTupleToStructCodeRefactoringService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.CopilotChangeAnalysis" ClassName="Microsoft.CodeAnalysis.Remote.RemoteCopilotChangeAnalysisService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.DependentTypeFinder" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDependentTypeFinderService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.DesignerAttributeDiscovery" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDesignerAttributeDiscoveryService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.DiagnosticAnalyzer" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDiagnosticAnalyzerService+Factory" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeCleanup;
[ExportLanguageService(typeof(ICodeCleanupService), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class CSharpCodeCleanupService(ICodeFixService codeFixService, IDiagnosticAnalyzerService diagnosticAnalyzerService) : AbstractCodeCleanupService(codeFixService, diagnosticAnalyzerService)
internal sealed class CSharpCodeCleanupService(ICodeFixService codeFixService)
: AbstractCodeCleanupService(codeFixService)
{
/// <summary>
/// Maps format document code cleanup options to DiagnosticId[]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// 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.Options;
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 IGlobalOptionService _globalOptions;
private readonly IThreadingContext _threadingContext;
private readonly Lazy<SuggestionServiceBase> _suggestionServiceBase;
private readonly IAsynchronousOperationListener _listener;

private readonly AsyncBatchingWorkQueue<(bool accepted, ProposalBase proposal)> _completionWorkQueue;

private int _started;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public CopilotWpfTextViewCreationListener(
IGlobalOptionService globalOptions,
IThreadingContext threadingContext,
Lazy<SuggestionServiceBase> suggestionServiceBase,
IAsynchronousOperationListenerProvider listenerProvider)
{
_globalOptions = globalOptions;
_threadingContext = threadingContext;
_suggestionServiceBase = suggestionServiceBase;
_listener = listenerProvider.GetListener(FeatureAttribute.CopilotChangeAnalysis);

_completionWorkQueue = new AsyncBatchingWorkQueue<(bool accepted, ProposalBase proposal)>(
DelayTimeSpan.Idle,
ProcessCompletionEventsAsync,
_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 += OnCompletionSuggestionAccepted;
suggestionService.SuggestionDismissed += OnCompletionSuggestionDismissed;
}).CompletesAsyncOperation(token);
}
}

private void OnCompletionSuggestionAccepted(object sender, SuggestionAcceptedEventArgs e)
=> OnCompletionSuggestionEvent(accepted: true, e.FinalProposal);

private void OnCompletionSuggestionDismissed(object sender, SuggestionDismissedEventArgs e)
=> OnCompletionSuggestionEvent(accepted: false, e.FinalProposal);

private void OnCompletionSuggestionEvent(bool accepted, ProposalBase? proposal)
{
if (proposal is not { Edits.Count: > 0 })
return;

_completionWorkQueue.AddWork((accepted, proposal));
}

private async ValueTask ProcessCompletionEventsAsync(
ImmutableSegmentedList<(bool accepted, ProposalBase proposal)> list, CancellationToken cancellationToken)
{
// Ignore if analyzing changes is disabled for this user.
if (!_globalOptions.GetOption(CopilotOptions.AnalyzeCopilotChanges))
return;

foreach (var (accepted, proposal) in list)
await ProcessCompletionEventAsync(accepted, proposal, cancellationToken).ConfigureAwait(false);
}

private static async ValueTask ProcessCompletionEventAsync(
bool accepted, ProposalBase proposal, CancellationToken cancellationToken)
{
const string featureId = "Completion";
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;

using var _ = PooledObjects.ArrayBuilder<TextChange>.GetInstance(out var textChanges);
foreach (var edit in editGroup)
textChanges.Add(new TextChange(edit.Span.Span.ToTextSpan(), edit.ReplacementText));

await CopilotChangeAnalysisUtilities.AnalyzeCopilotChangeAsync(
document, accepted, featureId, proposalId, textChanges, cancellationToken).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,12 @@ internal abstract partial class AbstractDiagnosticsTaggerProvider<TTag>
/// </summary>
private sealed class SingleDiagnosticKindPullTaggerProvider(
AbstractDiagnosticsTaggerProvider<TTag> callback,
IDiagnosticAnalyzerService analyzerService,
DiagnosticKind diagnosticKind,
TaggerHost taggerHost,
string featureName)
: AsynchronousTaggerProvider<TTag>(taggerHost, featureName)
{
private readonly DiagnosticKind _diagnosticKind = diagnosticKind;
private readonly IDiagnosticAnalyzerService _analyzerService = analyzerService;

// The following three fields are used to help calculate diagnostic performance for syntax errors upon file open.
// During TagsChanged notification for syntax errors, VSPlatform will check the buffer's property bag for a
Expand Down Expand Up @@ -104,7 +102,8 @@ private async Task ProduceTagsAsync(
var snapshot = documentSpanToTag.SnapshotSpan.Snapshot;

var project = document.Project;
var workspace = project.Solution.Workspace;
var solution = project.Solution;
var workspace = solution.Workspace;

// See if we've marked any spans as those we want to suppress diagnostics for.
// This can happen for buffers used in the preview workspace where some feature
Expand All @@ -124,7 +123,8 @@ private async Task ProduceTagsAsync(
// NOTE: We pass 'includeSuppressedDiagnostics: true' to ensure that IDE0079 (unnecessary suppressions)
// are flagged and faded in the editor. IDE0079 analyzer requires all source suppressed diagnostics to
// be provided to it to function correctly.
var diagnostics = await _analyzerService.GetDiagnosticsForSpanAsync(
var analyzerService = solution.Services.GetRequiredService<IDiagnosticAnalyzerService>();
var diagnostics = await analyzerService.GetDiagnosticsForSpanAsync(
document,
requestedSpan.Span.ToTextSpan(),
diagnosticKind: _diagnosticKind,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ internal abstract partial class AbstractDiagnosticsTaggerProvider<TTag> : ITagge
private readonly ImmutableArray<SingleDiagnosticKindPullTaggerProvider> _diagnosticsTaggerProviders;

public AbstractDiagnosticsTaggerProvider(
IDiagnosticAnalyzerService analyzerService,
TaggerHost taggerHost,
string featureName)
{
Expand All @@ -48,7 +47,7 @@ public AbstractDiagnosticsTaggerProvider(
return;

SingleDiagnosticKindPullTaggerProvider CreateDiagnosticsTaggerProvider(DiagnosticKind diagnosticKind)
=> new(this, analyzerService, diagnosticKind, taggerHost, featureName);
=> new(this, diagnosticKind, taggerHost, featureName);
}

// Functionality for subclasses to control how this diagnostic tagging operates. All the individual
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,12 @@ namespace Microsoft.CodeAnalysis.Editor.InlineDiagnostics;
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class InlineDiagnosticsTaggerProvider(
IDiagnosticAnalyzerService analyzerService,
TaggerHost taggerHost,
IEditorFormatMapService editorFormatMapService,
IClassificationFormatMapService classificationFormatMapService,
IClassificationTypeRegistryService classificationTypeRegistryService)
: AbstractDiagnosticsTaggerProvider<InlineDiagnosticsTag>(
analyzerService,
taggerHost,
FeatureAttribute.ErrorSquiggles)
taggerHost, FeatureAttribute.ErrorSquiggles)
{
private readonly IEditorFormatMap _editorFormatMap = editorFormatMapService.GetEditorFormatMap("text");
private readonly IClassificationFormatMapService _classificationFormatMapService = classificationFormatMapService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@ public AnalyzerSettingsProvider(
string fileName,
AnalyzerSettingsUpdater settingsUpdater,
Workspace workspace,
IDiagnosticAnalyzerService analyzerService,
IGlobalOptionService optionService)
: base(fileName, settingsUpdater, workspace, optionService)
{
_analyzerService = analyzerService;
_analyzerService = workspace.Services.GetRequiredService<IDiagnosticAnalyzerService>();
Update();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Analyz

internal sealed class AnalyzerSettingsProviderFactory(
Workspace workspace,
IDiagnosticAnalyzerService analyzerService,
IGlobalOptionService globalOptionService) : IWorkspaceSettingsProviderFactory<AnalyzerSetting>
{
public ISettingsProvider<AnalyzerSetting> GetForFile(string filePath)
{
var updater = new AnalyzerSettingsUpdater(workspace, filePath);
return new AnalyzerSettingsProvider(filePath, updater, workspace, analyzerService, globalOptionService);
return new AnalyzerSettingsProvider(filePath, updater, workspace, globalOptionService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Analyz
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class AnalyzerSettingsWorkspaceServiceFactory(
IDiagnosticAnalyzerService analyzerService,
IGlobalOptionService globalOptionService) : IWorkspaceServiceFactory
{
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
=> new AnalyzerSettingsProviderFactory(workspaceServices.Workspace, analyzerService, globalOptionService);
=> new AnalyzerSettingsProviderFactory(workspaceServices.Workspace, globalOptionService);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript;
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class VSTypeScriptDocumentPullDiagnosticHandlerFactory(
IDiagnosticAnalyzerService analyzerService,
IDiagnosticSourceManager diagnosticSourceManager,
IDiagnosticsRefresher diagnosticsRefresher,
IGlobalOptionService globalOptions) : DocumentPullDiagnosticHandlerFactory(analyzerService, diagnosticSourceManager, diagnosticsRefresher, globalOptions)
IGlobalOptionService globalOptions) : DocumentPullDiagnosticHandlerFactory(diagnosticSourceManager, diagnosticsRefresher, globalOptions)
{
}

Expand All @@ -29,9 +28,8 @@ internal class VSTypeScriptDocumentPullDiagnosticHandlerFactory(
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class VSTypeScriptWorkspacePullDiagnosticHandler(
LspWorkspaceRegistrationService registrationService,
IDiagnosticAnalyzerService analyzerService,
IDiagnosticSourceManager diagnosticSourceManager,
IDiagnosticsRefresher diagnosticsRefresher,
IGlobalOptionService globalOptions) : WorkspacePullDiagnosticHandlerFactory(registrationService, analyzerService, diagnosticSourceManager, diagnosticsRefresher, globalOptions)
IGlobalOptionService globalOptions) : WorkspacePullDiagnosticHandlerFactory(registrationService, diagnosticSourceManager, diagnosticsRefresher, globalOptions)
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ private sealed class StateMachine

private readonly IInlineRenameService _inlineRenameService;
private readonly IAsynchronousOperationListener _asyncListener;
private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService;

// Store committed sessions so they can be restored on undo/redo. The undo transactions
// may live beyond the lifetime of the buffer tracked by this StateMachine, so storing
Expand All @@ -58,7 +57,6 @@ public StateMachine(
IThreadingContext threadingContext,
ITextBuffer buffer,
IInlineRenameService inlineRenameService,
IDiagnosticAnalyzerService diagnosticAnalyzerService,
IGlobalOptionService globalOptions,
IAsynchronousOperationListener asyncListener)
{
Expand All @@ -67,7 +65,6 @@ public StateMachine(
Buffer.Changed += Buffer_Changed;
_inlineRenameService = inlineRenameService;
_asyncListener = asyncListener;
_diagnosticAnalyzerService = diagnosticAnalyzerService;
GlobalOptions = globalOptions;
}

Expand Down Expand Up @@ -238,8 +235,8 @@ public bool ClearVisibleTrackingSession()
// provide a diagnostic/codefix, but nothing has changed in the workspace
// to trigger the diagnostic system to reanalyze, so we trigger it
// manually.

_diagnosticAnalyzerService?.RequestDiagnosticRefresh();
var service = document.Project.Solution.Services.GetRequiredService<IDiagnosticAnalyzerService>();
service.RequestDiagnosticRefresh();
}

// Disallow the existing TrackingSession from triggering IdentifierFound.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,17 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking;
internal sealed partial class RenameTrackingTaggerProvider(
IThreadingContext threadingContext,
IInlineRenameService inlineRenameService,
IDiagnosticAnalyzerService diagnosticAnalyzerService,
IGlobalOptionService globalOptions,
IAsynchronousOperationListenerProvider listenerProvider) : ITaggerProvider
{
private readonly IThreadingContext _threadingContext = threadingContext;
private readonly IAsynchronousOperationListener _asyncListener = listenerProvider.GetListener(FeatureAttribute.RenameTracking);
private readonly IInlineRenameService _inlineRenameService = inlineRenameService;
private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService = diagnosticAnalyzerService;
private readonly IGlobalOptionService _globalOptions = globalOptions;

public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
var stateMachine = buffer.Properties.GetOrCreateSingletonProperty(() => new StateMachine(_threadingContext, buffer, _inlineRenameService, _diagnosticAnalyzerService, _globalOptions, _asyncListener));
var stateMachine = buffer.Properties.GetOrCreateSingletonProperty(() => new StateMachine(_threadingContext, buffer, _inlineRenameService, _globalOptions, _asyncListener));
return new Tagger(stateMachine) as ITagger<T>;
}

Expand Down
Loading
Loading