提交 647eb316 编写于 作者: S Sam Harwell

Remove UI thread filtering of code actions

Closes #46431
上级 bbfee62c
......@@ -301,9 +301,9 @@ static SuggestedActionSetPriority ConvertToSuggestedActionSetPriority(UnifiedSug
// See https://github.com/dotnet/roslyn/issues/29589
const bool includeSuppressionFixes = true;
return UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixes_MustBeCalledFromUIThread(
return UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync(
workspace, _owner._codeFixService, document, range.Span.ToTextSpan(),
includeSuppressionFixes, isBlocking: true, addOperationScope, cancellationToken);
includeSuppressionFixes, isBlocking: true, addOperationScope, cancellationToken).WaitAndGetResult(cancellationToken);
}
private static string GetFixCategory(DiagnosticSeverity severity)
......@@ -352,9 +352,9 @@ private static string GetFixCategory(DiagnosticSeverity severity)
// then we want to filter out refactorings outside the selection span.
var filterOutsideSelection = !requestedActionCategories.Contains(PredefinedSuggestedActionCategoryNames.Refactoring);
return UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactorings_MustBeCalledFromUIThread(
return UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync(
workspace, _owner._codeRefactoringService, document, selection, isBlocking: true,
addOperationScope, filterOutsideSelection, cancellationToken);
addOperationScope, filterOutsideSelection, cancellationToken).WaitAndGetResult(cancellationToken);
}
public Task<bool> HasSuggestedActionsAsync(
......
......@@ -743,26 +743,8 @@ private async Task<IEnumerable<Diagnostic>> GetProjectDiagnosticsAsync(Project p
foreach (var fixer in allFixers)
{
await extensionManager.PerformActionAsync(fixer, () => fixer.RegisterCodeFixesAsync(context) ?? Task.CompletedTask).ConfigureAwait(false);
foreach (var fix in fixes)
{
if (!fix.Action.PerformFinalApplicabilityCheck)
{
return true;
}
// Have to see if this fix is still applicable. Jump to the foreground thread
// to make that check.
await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, cancellationToken);
var applicable = fix.Action.IsApplicable(document.Project.Solution.Workspace);
await TaskScheduler.Default;
if (applicable)
{
return true;
}
}
if (fixes.Count > 0)
return true;
}
return false;
......
......@@ -224,7 +224,7 @@ End Namespace
Await TestAsync(input, expected, codeActionIndex:=1)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)>
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)>
Public Async Function AddProjectReference_CSharpToCSharp_Test() As Task
Dim input =
<Workspace>
......@@ -305,7 +305,7 @@ namespace CSAssembly2
Await TestMissing(input)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)>
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)>
Public Async Function TestAddProjectReference_CSharpToCSharp_WithProjectRenamed() As Task
Dim input =
<Workspace>
......@@ -358,7 +358,7 @@ namespace CSAssembly2
End Sub)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)>
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)>
Public Async Function TestAddProjectReference_VBToVB() As Task
Dim input =
<Workspace>
......@@ -449,7 +449,7 @@ class C
Await TestMissing(input)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)>
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)>
<WorkItem(16022, "https://github.com/dotnet/roslyn/issues/16022")>
Public Async Function TestAddProjectReference_EvenWithExistingUsing() As Task
Dim input =
......
......@@ -9,9 +9,11 @@
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.MetadataAsSource;
......@@ -707,6 +709,15 @@ public void ChangeSolution(Solution solution)
public override bool CanApplyParseOptionChange(ParseOptions oldOptions, ParseOptions newOptions, Project project)
=> true;
internal override async Task<bool> CanAddProjectReferenceAsync(ProjectId referencingProject, ProjectId referencedProject, CancellationToken cancellationToken)
{
// VisualStudioWorkspace switches to the main thread for this call, so do the same thing here to catch tests
// that fail to account for this possibility.
var threadingContext = ExportProvider.GetExportedValue<IThreadingContext>();
await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, cancellationToken);
return true;
}
internal ITextBuffer GetOrCreateBufferForPath(string? filePath, IContentType contentType, string languageName, string initialText)
{
// If we don't have a file path we'll just make something up for the purpose of this dictionary so all
......
......@@ -2,7 +2,8 @@
// 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;
#nullable enable
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
......@@ -16,47 +17,48 @@ internal abstract partial class AbstractAddImportFeatureService<TSimpleNameSynta
{
private class AssemblyReferenceCodeAction : AddImportCodeAction
{
private readonly Lazy<string> _lazyResolvedPath;
public AssemblyReferenceCodeAction(
Document originalDocument,
AddImportFixData fixData)
: base(originalDocument, fixData)
{
Contract.ThrowIfFalse(fixData.Kind == AddImportFixKind.ReferenceAssemblySymbol);
_lazyResolvedPath = new Lazy<string>(ResolvePath);
}
private string ResolvePath()
private Task<string?> ResolvePathAsync(CancellationToken cancellationToken)
{
var assemblyResolverService = OriginalDocument.Project.Solution.Workspace.Services.GetService<IFrameworkAssemblyPathResolver>();
var assemblyResolverService = OriginalDocument.Project.Solution.Workspace.Services.GetRequiredService<IFrameworkAssemblyPathResolver>();
var assemblyPath = assemblyResolverService?.ResolveAssemblyPath(
return assemblyResolverService.ResolveAssemblyPathAsync(
OriginalDocument.Project.Id,
FixData.AssemblyReferenceAssemblyName,
FixData.AssemblyReferenceFullyQualifiedTypeName);
return assemblyPath;
FixData.AssemblyReferenceFullyQualifiedTypeName,
cancellationToken);
}
internal override bool PerformFinalApplicabilityCheck
=> true;
protected override Task<IEnumerable<CodeActionOperation>> ComputePreviewOperationsAsync(CancellationToken cancellationToken)
=> ComputeOperationsAsync(isPreview: true, cancellationToken);
internal override bool IsApplicable(Workspace workspace)
=> !string.IsNullOrWhiteSpace(_lazyResolvedPath.Value);
protected override Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(CancellationToken cancellationToken)
=> ComputeOperationsAsync(isPreview: false, cancellationToken);
protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(CancellationToken cancellationToken)
private async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(bool isPreview, CancellationToken cancellationToken)
{
var service = OriginalDocument.Project.Solution.Workspace.Services.GetService<IMetadataService>();
var resolvedPath = _lazyResolvedPath.Value;
var reference = service.GetReference(resolvedPath, MetadataReferenceProperties.Assembly);
var newDocument = await GetUpdatedDocumentAsync(cancellationToken).ConfigureAwait(false);
var newProject = newDocument.Project;
// Now add the actual assembly reference.
var newProject = newDocument.Project;
newProject = newProject.WithMetadataReferences(
newProject.MetadataReferences.Concat(reference));
if (!isPreview)
{
var resolvedPath = await ResolvePathAsync(cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(resolvedPath))
{
var service = OriginalDocument.Project.Solution.Workspace.Services.GetRequiredService<IMetadataService>();
var reference = service.GetReference(resolvedPath, MetadataReferenceProperties.Assembly);
newProject = newProject.WithMetadataReferences(
newProject.MetadataReferences.Concat(reference));
}
}
var operation = new ApplyChangesOperation(newProject.Solution);
return SpecializedCollections.SingletonEnumerable<CodeActionOperation>(operation);
......
......@@ -3,6 +3,8 @@
// See the LICENSE file in the project root for more information.
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.AddImport
......@@ -17,14 +19,14 @@ public MetadataSymbolReferenceCodeAction(Document originalDocument, AddImportFix
Contract.ThrowIfFalse(fixData.Kind == AddImportFixKind.MetadataSymbol);
}
protected override Project UpdateProject(Project project)
protected override Task<Project> UpdateProjectAsync(Project project, bool isPreview, CancellationToken cancellationToken)
{
var projectWithReference = project.Solution.GetProject(FixData.PortableExecutableReferenceProjectId);
var reference = projectWithReference.MetadataReferences
.OfType<PortableExecutableReference>()
.First(pe => pe.FilePath == FixData.PortableExecutableReferenceFilePathToAdd);
return project.AddMetadataReference(reference);
return Task.FromResult(project.AddMetadataReference(reference));
}
}
}
......
......@@ -2,6 +2,8 @@
// 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.Threading;
using System.Threading.Tasks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.AddImport
......@@ -27,19 +29,22 @@ private class ProjectSymbolReferenceCodeAction : SymbolReferenceCodeAction
private bool ShouldAddProjectReference()
=> FixData.ProjectReferenceToAdd != null && FixData.ProjectReferenceToAdd != OriginalDocument.Project.Id;
internal override bool PerformFinalApplicabilityCheck
=> ShouldAddProjectReference();
protected override async Task<Project> UpdateProjectAsync(Project project, bool isPreview, CancellationToken cancellationToken)
{
if (!ShouldAddProjectReference())
{
return project;
}
internal override bool IsApplicable(Workspace workspace)
=> ShouldAddProjectReference() &&
workspace.CanAddProjectReference(
OriginalDocument.Project.Id, FixData.ProjectReferenceToAdd);
if (!isPreview)
{
if (!await project.Solution.Workspace.CanAddProjectReferenceAsync(OriginalDocument.Project.Id, FixData.ProjectReferenceToAdd, cancellationToken).ConfigureAwait(false))
{
return project;
}
}
protected override Project UpdateProject(Project project)
{
return ShouldAddProjectReference()
? project.AddProjectReference(new ProjectReference(FixData.ProjectReferenceToAdd))
: project;
return project.AddProjectReference(new ProjectReference(FixData.ProjectReferenceToAdd));
}
}
}
......
......@@ -2,8 +2,11 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
namespace Microsoft.CodeAnalysis.AddImport
{
......@@ -24,18 +27,34 @@ private abstract class SymbolReferenceCodeAction : AddImportCodeAction
{
}
protected override async Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
protected override async Task<IEnumerable<CodeActionOperation>> ComputePreviewOperationsAsync(CancellationToken cancellationToken)
{
var changedSolution = await GetChangedSolutionAsync(isPreview: true, cancellationToken).ConfigureAwait(false);
if (changedSolution == null)
{
return Array.Empty<CodeActionOperation>();
}
return new CodeActionOperation[] { new ApplyChangesOperation(changedSolution) };
}
protected override Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
{
return GetChangedSolutionAsync(isPreview: false, cancellationToken);
}
private async Task<Solution> GetChangedSolutionAsync(bool isPreview, CancellationToken cancellationToken)
{
var updatedDocument = await GetUpdatedDocumentAsync(cancellationToken).ConfigureAwait(false);
// Defer to subtype to add any p2p or metadata refs as appropriate.
var updatedProject = UpdateProject(updatedDocument.Project);
var updatedProject = await UpdateProjectAsync(updatedDocument.Project, isPreview, cancellationToken).ConfigureAwait(false);
var updatedSolution = updatedProject.Solution;
return updatedSolution;
}
protected abstract Project UpdateProject(Project project);
protected abstract Task<Project> UpdateProjectAsync(Project project, bool isPreview, CancellationToken cancellationToken);
}
}
}
......@@ -38,8 +38,6 @@ public MoveFileCodeAction(State state, ImmutableArray<string> newFolders)
_newfolders = newFolders;
}
internal override bool PerformFinalApplicabilityCheck => true;
protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(CancellationToken cancellationToken)
{
var id = _state.Document.Id;
......
......@@ -29,10 +29,9 @@ namespace Microsoft.CodeAnalysis.UnifiedSuggestions
internal class UnifiedSuggestedActionsSource
{
/// <summary>
/// Gets, filters, and orders code fixes. Must be called from the UI thread
/// due to the eventual call to <see cref="CodeAction.IsApplicable(Workspace)"/>.
/// Gets, filters, and orders code fixes.
/// </summary>
public static ImmutableArray<UnifiedSuggestedActionSet> GetFilterAndOrderCodeFixes_MustBeCalledFromUIThread(
public static async Task<ImmutableArray<UnifiedSuggestedActionSet>> GetFilterAndOrderCodeFixesAsync(
Workspace workspace,
ICodeFixService codeFixService,
Document document,
......@@ -46,30 +45,25 @@ internal class UnifiedSuggestedActionsSource
// it. However, it's deliberate. We want to make sure that the code runs on
// the background so that no one takes an accidentally dependency on running on
// the UI thread.
var fixes = Task.Run(
var fixes = await Task.Run(
() => codeFixService.GetFixesAsync(
document, selection, includeSuppressionFixes, isBlocking,
addOperationScope, cancellationToken), cancellationToken).WaitAndGetResult(cancellationToken);
addOperationScope, cancellationToken), cancellationToken).ConfigureAwait(false);
var filteredFixes = FilterOnUIThread(fixes, workspace);
var filteredFixes = FilterOnAnyThread(fixes);
var organizedFixes = OrganizeFixes(workspace, filteredFixes, includeSuppressionFixes);
return organizedFixes;
}
private static ImmutableArray<CodeFixCollection> FilterOnUIThread(
ImmutableArray<CodeFixCollection> collections,
Workspace workspace)
private static ImmutableArray<CodeFixCollection> FilterOnAnyThread(ImmutableArray<CodeFixCollection> collections)
{
return collections.Select(
c => FilterIndividuallyOnUIThread(c, workspace)).WhereNotNull().ToImmutableArray();
return collections.Select(c => FilterIndividuallyOnAnyThread(c)).WhereNotNull().ToImmutableArray();
}
private static CodeFixCollection? FilterIndividuallyOnUIThread(
CodeFixCollection collection,
Workspace workspace)
private static CodeFixCollection? FilterIndividuallyOnAnyThread(CodeFixCollection collection)
{
var applicableFixes = collection.Fixes.WhereAsArray(f => IsApplicable(f.Action, workspace));
var applicableFixes = collection.Fixes;
return applicableFixes.Length == 0
? null
: applicableFixes.Length == collection.Fixes.Length
......@@ -411,10 +405,9 @@ private static bool IsBulkConfigurationAction(CodeAction action)
=> (action as AbstractConfigurationActionWithNestedActions)?.IsBulkConfigurationAction == true;
/// <summary>
/// Gets, filters, and orders code refactorings. Must be called from the UI thread
/// due to the eventual call to <see cref="CodeAction.IsApplicable(Workspace)"/>.
/// Gets, filters, and orders code refactorings.
/// </summary>
public static ImmutableArray<UnifiedSuggestedActionSet> GetFilterAndOrderCodeRefactorings_MustBeCalledFromUIThread(
public static async Task<ImmutableArray<UnifiedSuggestedActionSet>> GetFilterAndOrderCodeRefactoringsAsync(
Workspace workspace,
ICodeRefactoringService codeRefactoringService,
Document document,
......@@ -428,30 +421,27 @@ private static bool IsBulkConfigurationAction(CodeAction action)
// it. However, it's deliberate. We want to make sure that the code runs on
// the background so that no one takes an accidentally dependency on running on
// the UI thread.
var refactorings = Task.Run(
var refactorings = await Task.Run(
() => codeRefactoringService.GetRefactoringsAsync(
document, selection, isBlocking, addOperationScope,
cancellationToken), cancellationToken).WaitAndGetResult(cancellationToken);
cancellationToken), cancellationToken).ConfigureAwait(false);
var filteredRefactorings = FilterOnUIThread(refactorings, selection, filterOutsideSelection, workspace);
var filteredRefactorings = FilterOnAnyThread(refactorings, selection, filterOutsideSelection);
return filteredRefactorings.SelectAsArray(
r => OrganizeRefactorings(workspace, r));
}
private static ImmutableArray<CodeRefactoring> FilterOnUIThread(
private static ImmutableArray<CodeRefactoring> FilterOnAnyThread(
ImmutableArray<CodeRefactoring> refactorings,
TextSpan selection,
bool filterOutsideSelection,
Workspace workspace)
=> refactorings.Select(r => FilterOnUIThread(
r, selection, filterOutsideSelection, workspace)).WhereNotNull().ToImmutableArray();
bool filterOutsideSelection)
=> refactorings.Select(r => FilterOnAnyThread(r, selection, filterOutsideSelection)).WhereNotNull().ToImmutableArray();
private static CodeRefactoring? FilterOnUIThread(
private static CodeRefactoring? FilterOnAnyThread(
CodeRefactoring refactoring,
TextSpan selection,
bool filterOutsideSelection,
Workspace workspace)
bool filterOutsideSelection)
{
var actions = refactoring.CodeActions.WhereAsArray(IsActionAndSpanApplicable);
return actions.Length == 0
......@@ -462,11 +452,6 @@ private static bool IsBulkConfigurationAction(CodeAction action)
bool IsActionAndSpanApplicable((CodeAction action, TextSpan? applicableSpan) actionAndSpan)
{
if (!IsApplicable(actionAndSpan.action, workspace))
{
return false;
}
if (filterOutsideSelection)
{
// Filter out refactorings with applicable span outside the selection span.
......@@ -539,19 +524,6 @@ private static UnifiedSuggestedActionSet OrganizeRefactorings(Workspace workspac
applicableToSpan: refactoring.CodeActions.FirstOrDefault().applicableToSpan);
}
private static bool IsApplicable(CodeAction action, Workspace workspace)
{
if (!action.PerformFinalApplicabilityCheck)
{
// If we don't even need to perform the final applicability check,
// then the code action is applicable.
return true;
}
// Otherwise, defer to the action to make the decision.
return action.IsApplicable(workspace);
}
private static UnifiedSuggestedActionSetPriority GetUnifiedSuggestedActionSetPriority(CodeActionPriority key)
=> key switch
{
......@@ -564,8 +536,8 @@ private static UnifiedSuggestedActionSetPriority GetUnifiedSuggestedActionSetPri
/// <summary>
/// Filters and orders the code fix sets and code refactoring sets amongst each other.
/// Should be called with the results from <see cref="GetFilterAndOrderCodeFixes_MustBeCalledFromUIThread"/>
/// and <see cref="GetFilterAndOrderCodeRefactorings_MustBeCalledFromUIThread"/>.
/// Should be called with the results from <see cref="GetFilterAndOrderCodeFixesAsync"/>
/// and <see cref="GetFilterAndOrderCodeRefactoringsAsync"/>.
/// </summary>
public static ImmutableArray<UnifiedSuggestedActionSet>? FilterAndOrderActionSets(
ImmutableArray<UnifiedSuggestedActionSet> fixes,
......
......@@ -11,7 +11,6 @@
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.UnifiedSuggestions;
using Microsoft.VisualStudio.LanguageServer.Protocol;
......@@ -35,12 +34,11 @@ internal static class CodeActionHelpers
Document document,
ICodeFixService codeFixService,
ICodeRefactoringService codeRefactoringService,
IThreadingContext threadingContext,
LSP.Range selection,
CancellationToken cancellationToken)
{
var actionSets = await GetActionSetsAsync(
document, codeFixService, codeRefactoringService, threadingContext, selection, cancellationToken).ConfigureAwait(false);
document, codeFixService, codeRefactoringService, selection, cancellationToken).ConfigureAwait(false);
if (!actionSets.HasValue)
{
return Array.Empty<VSCodeAction>();
......@@ -108,12 +106,11 @@ internal static class CodeActionHelpers
Document document,
ICodeFixService codeFixService,
ICodeRefactoringService codeRefactoringService,
IThreadingContext threadingContext,
LSP.Range selection,
CancellationToken cancellationToken)
{
var actionSets = await GetActionSetsAsync(
document, codeFixService, codeRefactoringService, threadingContext, selection, cancellationToken).ConfigureAwait(false);
document, codeFixService, codeRefactoringService, selection, cancellationToken).ConfigureAwait(false);
if (!actionSets.HasValue)
{
return ImmutableArray<CodeAction>.Empty;
......@@ -165,23 +162,19 @@ private static CodeAction GetNestedActionsFromActionSet(IUnifiedSuggestedAction
Document document,
ICodeFixService codeFixService,
ICodeRefactoringService codeRefactoringService,
IThreadingContext threadingContext,
LSP.Range selection,
CancellationToken cancellationToken)
{
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var textSpan = ProtocolConversions.RangeToTextSpan(selection, text);
// The logic to filter code actions requires the UI thread
await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
var codeFixes = UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixes_MustBeCalledFromUIThread(
var codeFixes = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync(
document.Project.Solution.Workspace, codeFixService, document, textSpan, includeSuppressionFixes: true,
isBlocking: false, addOperationScope: _ => null, cancellationToken);
isBlocking: false, addOperationScope: _ => null, cancellationToken).ConfigureAwait(false);
var codeRefactorings = UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactorings_MustBeCalledFromUIThread(
var codeRefactorings = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync(
document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, isBlocking: false,
addOperationScope: _ => null, filterOutsideSelection: false, cancellationToken);
addOperationScope: _ => null, filterOutsideSelection: false, cancellationToken).ConfigureAwait(false);
var actionSets = UnifiedSuggestedActionsSource.FilterAndOrderActionSets(codeFixes, codeRefactorings, textSpan);
return actionSets;
......
......@@ -6,7 +6,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
......@@ -14,7 +13,6 @@
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions;
using Microsoft.CodeAnalysis.PooledObjects;
......@@ -37,20 +35,17 @@ internal class CodeActionResolveHandler : AbstractRequestHandler<LSP.VSCodeActio
{
private readonly ICodeFixService _codeFixService;
private readonly ICodeRefactoringService _codeRefactoringService;
private readonly IThreadingContext _threadingContext;
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public CodeActionResolveHandler(
ICodeFixService codeFixService,
ICodeRefactoringService codeRefactoringService,
IThreadingContext threadingContext,
ILspSolutionProvider solutionProvider)
: base(solutionProvider)
{
_codeFixService = codeFixService;
_codeRefactoringService = codeRefactoringService;
_threadingContext = threadingContext;
}
public override async Task<LSP.VSCodeAction> HandleRequestAsync(LSP.VSCodeAction codeAction, RequestContext context, CancellationToken cancellationToken)
......@@ -63,7 +58,6 @@ public override async Task<LSP.VSCodeAction> HandleRequestAsync(LSP.VSCodeAction
document,
_codeFixService,
_codeRefactoringService,
_threadingContext,
data.Range,
cancellationToken).ConfigureAwait(false);
......
......@@ -10,7 +10,6 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions;
using Microsoft.VisualStudio.LanguageServer.Protocol;
......@@ -28,7 +27,6 @@ internal class CodeActionsHandler : AbstractRequestHandler<LSP.CodeActionParams,
{
private readonly ICodeFixService _codeFixService;
private readonly ICodeRefactoringService _codeRefactoringService;
private readonly IThreadingContext _threadingContext;
internal const string RunCodeActionCommandName = "Roslyn.RunCodeAction";
......@@ -37,13 +35,11 @@ internal class CodeActionsHandler : AbstractRequestHandler<LSP.CodeActionParams,
public CodeActionsHandler(
ICodeFixService codeFixService,
ICodeRefactoringService codeRefactoringService,
ILspSolutionProvider solutionProvider,
IThreadingContext threadingContext)
ILspSolutionProvider solutionProvider)
: base(solutionProvider)
{
_codeFixService = codeFixService;
_codeRefactoringService = codeRefactoringService;
_threadingContext = threadingContext;
}
public override async Task<LSP.VSCodeAction[]> HandleRequestAsync(LSP.CodeActionParams request, RequestContext context, CancellationToken cancellationToken)
......@@ -55,8 +51,8 @@ public override async Task<LSP.VSCodeAction[]> HandleRequestAsync(LSP.CodeAction
}
var codeActions = await CodeActionHelpers.GetVSCodeActionsAsync(
request, document, _codeFixService, _codeRefactoringService, _threadingContext,
request.Range, cancellationToken).ConfigureAwait(false);
request, document, _codeFixService, _codeRefactoringService, request.Range,
cancellationToken).ConfigureAwait(false);
return codeActions;
}
......
......@@ -55,7 +55,7 @@ public async Task<object> HandleRequestAsync(LSP.ExecuteCommandParams request, R
var document = _solutionProvider.GetDocument(runRequest.TextDocument);
var codeActions = await CodeActionHelpers.GetCodeActionsAsync(
document, _codeFixService, _codeRefactoringService,
_threadingContext, runRequest.Range, cancellationToken).ConfigureAwait(false);
runRequest.Range, cancellationToken).ConfigureAwait(false);
var actionToRun = CodeActionHelpers.GetCodeActionToResolve(runRequest.UniqueIdentifier, codeActions);
Contract.ThrowIfNull(actionToRun);
......
......@@ -2,11 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Composition;
using System.IO;
using System.Reflection;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
......@@ -46,12 +50,13 @@ public Service(IThreadingContext threadingContext, VisualStudioWorkspace workspa
_serviceProvider = serviceProvider;
}
public string ResolveAssemblyPath(
public async Task<string?> ResolveAssemblyPathAsync(
ProjectId projectId,
string assemblyName,
string fullyQualifiedTypeName = null)
string? fullyQualifiedTypeName,
CancellationToken cancellationToken)
{
this.AssertIsForeground();
await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
var assembly = ResolveAssembly(projectId, assemblyName);
if (assembly != null)
......
......@@ -13,6 +13,7 @@
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using EnvDTE;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
......@@ -1351,9 +1352,10 @@ public void EnsureEditableDocuments(IEnumerable<DocumentId> documents)
public void EnsureEditableDocuments(params DocumentId[] documents)
=> this.EnsureEditableDocuments((IEnumerable<DocumentId>)documents);
internal override bool CanAddProjectReference(ProjectId referencingProject, ProjectId referencedProject)
internal override async Task<bool> CanAddProjectReferenceAsync(ProjectId referencingProject, ProjectId referencedProject, CancellationToken cancellationToken)
{
_foregroundObject.AssertIsForeground();
await _foregroundObject.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
if (!TryGetHierarchy(referencingProject, out var referencingHierarchy) ||
!TryGetHierarchy(referencedProject, out var referencedHierarchy))
{
......
......@@ -288,17 +288,6 @@ protected virtual Task<Document> PostProcessChangesAsync(Document document, Canc
return document;
}
internal virtual bool PerformFinalApplicabilityCheck => false;
/// <summary>
/// Called by the CodeActions on the UI thread to determine if the CodeAction is still
/// applicable and should be presented to the user. CodeActions can override this if they
/// need to do any final checking that must be performed on the UI thread (for example
/// accessing and querying the Visual Studio DTE).
/// </summary>
internal virtual bool IsApplicable(Workspace workspace)
=> true;
#region Factories for standard code actions
/// <summary>
......
......@@ -2,9 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host.Mef;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Host
{
......@@ -31,10 +36,10 @@ public Service()
// return false;
//}
public string ResolveAssemblyPath(ProjectId projectId, string assemblyName, string fullyQualifiedTypeName = null)
public Task<string?> ResolveAssemblyPathAsync(ProjectId projectId, string assemblyName, string? fullyQualifiedTypeName, CancellationToken cancellationToken)
{
// Assembly path resolution not supported at the default workspace level.
return null;
return SpecializedTasks.Null<string>();
}
}
}
......
......@@ -2,6 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.CodeAnalysis.Host
{
/// <summary>
......@@ -19,7 +24,7 @@ internal interface IFrameworkAssemblyPathResolver : IWorkspaceService
/// exist in the assembly.</param>
/// <param name="projectId">The project context to search within.</param>
/// <param name="assemblyName">The name of the assembly to try to resolve.</param>
string ResolveAssemblyPath(ProjectId projectId, string assemblyName, string fullyQualifiedName = null);
Task<string?> ResolveAssemblyPathAsync(ProjectId projectId, string assemblyName, string? fullyQualifiedName, CancellationToken cancellationToken);
// bool CanResolveType(ProjectId projectId, string assemblyName, string fullyQualifiedTypeName);
}
......
......@@ -1154,8 +1154,8 @@ public virtual bool CanApplyChange(ApplyChangesKind feature)
/// Returns <see langword="true"/> if a reference to referencedProject can be added to
/// referencingProject. <see langword="false"/> otherwise.
/// </summary>
internal virtual bool CanAddProjectReference(ProjectId referencingProject, ProjectId referencedProject)
=> false;
internal virtual Task<bool> CanAddProjectReferenceAsync(ProjectId referencingProject, ProjectId referencedProject, CancellationToken cancellationToken)
=> SpecializedTasks.False;
/// <summary>
/// Apply changes made to a solution back to the workspace.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册