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

Remove UI thread filtering of code actions

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