-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrap calls to CompletionProvider extensions such that they go through our extension manager #70843
Changes from 10 commits
b050925
116382a
b90528c
131cf69
6b0f6d9
795bdd9
5fa2bc9
e27320a
bafd4b1
f6d240f
c06da34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,127 +4,121 @@ | |
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CodeRefactorings; | ||
using Microsoft.CodeAnalysis.Editor.Options; | ||
using Microsoft.CodeAnalysis.Completion; | ||
using Microsoft.CodeAnalysis.ErrorLogger; | ||
using Microsoft.CodeAnalysis.ErrorReporting; | ||
using Microsoft.CodeAnalysis.Extensions; | ||
using Microsoft.CodeAnalysis.Host; | ||
using Microsoft.CodeAnalysis.Host.Mef; | ||
using Microsoft.CodeAnalysis.Internal.Log; | ||
using Microsoft.CodeAnalysis.Options; | ||
using Microsoft.CodeAnalysis.Telemetry; | ||
using Microsoft.VisualStudio.Text; | ||
using Roslyn.Utilities; | ||
using static Microsoft.CodeAnalysis.Internal.Log.FunctionId; | ||
using static Microsoft.CodeAnalysis.Internal.Log.Logger; | ||
using static Microsoft.CodeAnalysis.RoslynAssemblyHelper; | ||
|
||
namespace Microsoft.CodeAnalysis.Editor | ||
namespace Microsoft.CodeAnalysis.Editor; | ||
|
||
[ExportWorkspaceServiceFactory(typeof(IExtensionManager), ServiceLayer.Editor), Shared] | ||
[method: ImportingConstructor] | ||
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] | ||
internal sealed class EditorLayerExtensionManager( | ||
[ImportMany] IEnumerable<IExtensionErrorHandler> errorHandlers) : IWorkspaceServiceFactory | ||
{ | ||
[ExportWorkspaceServiceFactory(typeof(IExtensionManager), ServiceLayer.Editor), Shared] | ||
[method: ImportingConstructor] | ||
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] | ||
internal class EditorLayerExtensionManager( | ||
IGlobalOptionService optionService, | ||
[ImportMany] IEnumerable<IExtensionErrorHandler> errorHandlers) : IWorkspaceServiceFactory | ||
{ | ||
private readonly List<IExtensionErrorHandler> _errorHandlers = errorHandlers.ToList(); | ||
private readonly IGlobalOptionService _optionService = optionService; | ||
private readonly ImmutableArray<IExtensionErrorHandler> _errorHandlers = errorHandlers.ToImmutableArray(); | ||
|
||
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) | ||
{ | ||
var errorReportingService = workspaceServices.GetRequiredService<IErrorReportingService>(); | ||
var errorLoggerService = workspaceServices.GetRequiredService<IErrorLoggerService>(); | ||
return new ExtensionManager(_optionService, errorReportingService, errorLoggerService, _errorHandlers); | ||
} | ||
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) | ||
{ | ||
var errorReportingService = workspaceServices.GetRequiredService<IErrorReportingService>(); | ||
var errorLoggerService = workspaceServices.GetRequiredService<IErrorLoggerService>(); | ||
return new ExtensionManager(errorReportingService, errorLoggerService, _errorHandlers); | ||
} | ||
|
||
internal class ExtensionManager( | ||
IGlobalOptionService globalOptions, | ||
IErrorReportingService errorReportingService, | ||
IErrorLoggerService errorLoggerService, | ||
List<IExtensionErrorHandler> errorHandlers) : AbstractExtensionManager | ||
internal sealed class ExtensionManager( | ||
IErrorReportingService errorReportingService, | ||
IErrorLoggerService errorLoggerService, | ||
ImmutableArray<IExtensionErrorHandler> errorHandlers) : AbstractExtensionManager | ||
{ | ||
protected override void HandleNonCancellationException(object provider, Exception exception) | ||
{ | ||
private readonly List<IExtensionErrorHandler> _errorHandlers = errorHandlers; | ||
private readonly IGlobalOptionService _globalOptions = globalOptions; | ||
private readonly IErrorReportingService _errorReportingService = errorReportingService; | ||
private readonly IErrorLoggerService _errorLoggerService = errorLoggerService; | ||
Debug.Assert(exception is not OperationCanceledException); | ||
|
||
public override void HandleException(object provider, Exception exception) | ||
if (provider is CodeFixProvider | ||
or CodeRefactoringProvider | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add QuickInfoProvider too? |
||
or CodeRefactorings.FixAllProvider | ||
or CodeFixes.FixAllProvider | ||
or CompletionProvider) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added CompletionProvider here. That way this participates in teh yellow-bar, telling the user about the completion provider that thrw the exception and if htey want to enable it. |
||
{ | ||
if (provider is CodeFixProvider or CodeFixes.FixAllProvider or CodeRefactoringProvider or CodeRefactorings.FixAllProvider) | ||
if (!IsIgnored(provider)) | ||
{ | ||
if (!IsIgnored(provider) && | ||
_globalOptions.GetOption(ExtensionManagerOptions.DisableCrashingExtensions)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this option was always true, and had no way to configure it. removed it. |
||
{ | ||
base.HandleException(provider, exception); | ||
|
||
var providerType = provider.GetType(); | ||
|
||
_errorReportingService?.ShowGlobalErrorInfo( | ||
message: string.Format(WorkspacesResources._0_encountered_an_error_and_has_been_disabled, providerType.Name), | ||
TelemetryFeatureName.GetExtensionName(providerType), | ||
exception, | ||
new InfoBarUI(WorkspacesResources.Show_Stack_Trace, InfoBarUI.UIKind.HyperLink, () => ShowDetailedErrorInfo(exception), closeAfterAction: false), | ||
new InfoBarUI(WorkspacesResources.Enable, InfoBarUI.UIKind.Button, () => | ||
{ | ||
EnableProvider(provider); | ||
LogEnableProvider(provider); | ||
}), | ||
new InfoBarUI(WorkspacesResources.Enable_and_ignore_future_errors, InfoBarUI.UIKind.Button, () => | ||
{ | ||
EnableProvider(provider); | ||
IgnoreProvider(provider); | ||
LogEnableAndIgnoreProvider(provider); | ||
}), | ||
new InfoBarUI(string.Empty, InfoBarUI.UIKind.Close, () => LogLeaveDisabled(provider))); | ||
} | ||
else | ||
{ | ||
LogAction(CodefixInfobar_ErrorIgnored, provider); | ||
} | ||
this.DisableProvider(provider); | ||
|
||
var providerType = provider.GetType(); | ||
|
||
errorReportingService?.ShowGlobalErrorInfo( | ||
message: string.Format(WorkspacesResources._0_encountered_an_error_and_has_been_disabled, providerType.Name), | ||
TelemetryFeatureName.GetExtensionName(providerType), | ||
exception, | ||
new InfoBarUI(WorkspacesResources.Show_Stack_Trace, InfoBarUI.UIKind.HyperLink, () => ShowDetailedErrorInfo(exception), closeAfterAction: false), | ||
new InfoBarUI(WorkspacesResources.Enable, InfoBarUI.UIKind.Button, () => | ||
{ | ||
EnableProvider(provider); | ||
LogEnableProvider(provider); | ||
}), | ||
new InfoBarUI(WorkspacesResources.Enable_and_ignore_future_errors, InfoBarUI.UIKind.Button, () => | ||
{ | ||
EnableProvider(provider); | ||
IgnoreProvider(provider); | ||
LogEnableAndIgnoreProvider(provider); | ||
}), | ||
new InfoBarUI(string.Empty, InfoBarUI.UIKind.Close, () => LogLeaveDisabled(provider))); | ||
} | ||
else | ||
{ | ||
if (_globalOptions.GetOption(ExtensionManagerOptions.DisableCrashingExtensions)) | ||
{ | ||
base.HandleException(provider, exception); | ||
} | ||
|
||
_errorHandlers.Do(h => h.HandleError(provider, exception)); | ||
LogAction(CodefixInfobar_ErrorIgnored, provider); | ||
} | ||
} | ||
else | ||
{ | ||
|
||
_errorLoggerService?.LogException(provider, exception); | ||
this.DisableProvider(provider); | ||
errorHandlers.Do(h => h.HandleError(provider, exception)); | ||
} | ||
|
||
private void ShowDetailedErrorInfo(Exception exception) | ||
=> _errorReportingService.ShowDetailedErrorInfo(exception); | ||
errorLoggerService?.LogException(provider, exception); | ||
} | ||
|
||
private void ShowDetailedErrorInfo(Exception exception) | ||
=> errorReportingService.ShowDetailedErrorInfo(exception); | ||
|
||
private static void LogLeaveDisabled(object provider) | ||
=> LogAction(CodefixInfobar_LeaveDisabled, provider); | ||
private static void LogLeaveDisabled(object provider) | ||
=> LogAction(CodefixInfobar_LeaveDisabled, provider); | ||
|
||
private static void LogEnableAndIgnoreProvider(object provider) | ||
=> LogAction(CodefixInfobar_EnableAndIgnoreFutureErrors, provider); | ||
private static void LogEnableAndIgnoreProvider(object provider) | ||
=> LogAction(CodefixInfobar_EnableAndIgnoreFutureErrors, provider); | ||
|
||
private static void LogEnableProvider(object provider) | ||
=> LogAction(CodefixInfobar_Enable, provider); | ||
private static void LogEnableProvider(object provider) | ||
=> LogAction(CodefixInfobar_Enable, provider); | ||
|
||
private static void LogAction(FunctionId functionId, object provider) | ||
private static void LogAction(FunctionId functionId, object provider) | ||
{ | ||
if (IsRoslynCodefix(provider)) | ||
{ | ||
if (IsRoslynCodefix(provider)) | ||
{ | ||
Log(functionId, $"Name: {provider.GetType().FullName} Assembly Version: {provider.GetType().Assembly.GetName().Version}"); | ||
} | ||
else | ||
{ | ||
Log(functionId); | ||
} | ||
Log(functionId, $"Name: {provider.GetType().FullName} Assembly Version: {provider.GetType().Assembly.GetName().Version}"); | ||
} | ||
else | ||
{ | ||
Log(functionId); | ||
} | ||
|
||
private static bool IsRoslynCodefix(object source) => HasRoslynPublicKey(source); | ||
} | ||
|
||
private static bool IsRoslynCodefix(object source) => HasRoslynPublicKey(source); | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,7 +160,7 @@ public async Task<ImmutableArray<CodeRefactoring>> GetRefactoringsAsync( | |
} | ||
} | ||
|
||
private async Task<CodeRefactoring?> GetRefactoringFromProviderAsync( | ||
private Task<CodeRefactoring?> GetRefactoringFromProviderAsync( | ||
TextDocument textDocument, | ||
TextSpan state, | ||
CodeRefactoringProvider provider, | ||
|
@@ -169,58 +169,43 @@ public async Task<ImmutableArray<CodeRefactoring>> GetRefactoringsAsync( | |
CodeActionOptionsProvider options, | ||
CancellationToken cancellationToken) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
if (extensionManager.IsDisabled(provider)) | ||
{ | ||
return null; | ||
} | ||
|
||
try | ||
{ | ||
using var _ = ArrayBuilder<(CodeAction action, TextSpan? applicableToSpan)>.GetInstance(out var actions); | ||
var context = new CodeRefactoringContext(textDocument, state, | ||
|
||
// TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? | ||
(action, applicableToSpan) => | ||
{ | ||
// Serialize access for thread safety - we don't know what thread the refactoring provider will call this delegate from. | ||
lock (actions) | ||
{ | ||
// Add the Refactoring Provider Name to the parent CodeAction's CustomTags. | ||
// Always add a name even in cases of 3rd party refactorings that do not export | ||
// name metadata. | ||
action.AddCustomTagAndTelemetryInfo(providerMetadata, provider); | ||
|
||
actions.Add((action, applicableToSpan)); | ||
} | ||
}, | ||
options, | ||
cancellationToken); | ||
|
||
var task = provider.ComputeRefactoringsAsync(context) ?? Task.CompletedTask; | ||
await task.ConfigureAwait(false); | ||
|
||
if (actions.Count == 0) | ||
return extensionManager.PerformFunctionAsync( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moved all explicit try/catch code with extension manager to use the existing helper functions that already do the right thing. |
||
provider, | ||
async () => | ||
{ | ||
return null; | ||
} | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
using var _ = ArrayBuilder<(CodeAction action, TextSpan? applicableToSpan)>.GetInstance(out var actions); | ||
var context = new CodeRefactoringContext(textDocument, state, | ||
|
||
var fixAllProviderInfo = extensionManager.PerformFunction( | ||
provider, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, provider, FixAllProviderInfo.Create), defaultValue: null); | ||
return new CodeRefactoring(provider, actions.ToImmutable(), fixAllProviderInfo, options); | ||
} | ||
catch (OperationCanceledException) | ||
{ | ||
// We don't want to catch operation canceled exceptions in the catch block | ||
// below. So catch is here and rethrow it. | ||
throw; | ||
} | ||
catch (Exception e) | ||
{ | ||
extensionManager.HandleException(provider, e); | ||
} | ||
// TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? | ||
(action, applicableToSpan) => | ||
{ | ||
// Serialize access for thread safety - we don't know what thread the refactoring provider will call this delegate from. | ||
lock (actions) | ||
{ | ||
// Add the Refactoring Provider Name to the parent CodeAction's CustomTags. | ||
// Always add a name even in cases of 3rd party refactorings that do not export | ||
// name metadata. | ||
action.AddCustomTagAndTelemetryInfo(providerMetadata, provider); | ||
|
||
actions.Add((action, applicableToSpan)); | ||
} | ||
}, | ||
options, | ||
cancellationToken); | ||
|
||
var task = provider.ComputeRefactoringsAsync(context) ?? Task.CompletedTask; | ||
await task.ConfigureAwait(false); | ||
|
||
if (actions.Count == 0) | ||
{ | ||
return null; | ||
} | ||
|
||
return null; | ||
var fixAllProviderInfo = extensionManager.PerformFunction( | ||
provider, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, provider, FixAllProviderInfo.Create), defaultValue: null); | ||
return new CodeRefactoring(provider, actions.ToImmutable(), fixAllProviderInfo, options); | ||
}, defaultValue: null); | ||
} | ||
|
||
private class ProjectCodeRefactoringProvider | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
view with whitespace off.