diff --git a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveDocumentNavigationService.cs b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveDocumentNavigationService.cs index 6eb8c4d3c5fba..3b900d1b0d77c 100644 --- a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveDocumentNavigationService.cs +++ b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveDocumentNavigationService.cs @@ -35,7 +35,7 @@ public Task CanNavigateToLineAndOffsetAsync(Workspace workspace, DocumentI public Task CanNavigateToPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken) => SpecializedTasks.False; - public async Task GetLocationForSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, NavigationOptions options, bool allowInvalidSpan, CancellationToken cancellationToken) + public async Task GetLocationForSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, bool allowInvalidSpan, CancellationToken cancellationToken) { await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); if (workspace is not InteractiveWindowWorkspace interactiveWorkspace) @@ -68,7 +68,7 @@ public Task CanNavigateToPositionAsync(Workspace workspace, DocumentId doc return null; } - return new NavigableLocation(async cancellationToken => + return new NavigableLocation(async (options, cancellationToken) => { await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -87,10 +87,10 @@ public Task CanNavigateToPositionAsync(Workspace workspace, DocumentId doc }); } - public Task GetLocationForLineAndOffsetAsync(Workspace workspace, DocumentId documentId, int lineNumber, int offset, NavigationOptions options, CancellationToken cancellationToken) + public Task GetLocationForLineAndOffsetAsync(Workspace workspace, DocumentId documentId, int lineNumber, int offset, CancellationToken cancellationToken) => SpecializedTasks.Null(); - public Task GetLocationForPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, NavigationOptions options, CancellationToken cancellationToken) + public Task GetLocationForPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken) => SpecializedTasks.Null(); } } diff --git a/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbol.cs b/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbol.cs index 01b9261176284..e9955580f574a 100644 --- a/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbol.cs +++ b/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbol.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; @@ -79,7 +80,7 @@ private async Task NavigateAsync() _definitions, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(new NavigationOptions(PreferProvisionalTab: true, ActivateTab: true), cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemDisplay.cs b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemDisplay.cs index 0ca972c6646fe..f27a483776f45 100644 --- a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemDisplay.cs +++ b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemDisplay.cs @@ -123,11 +123,10 @@ public void NavigateTo() workspace, document.Id, _searchResult.NavigableItem.SourceSpan, - NavigationOptions.Default, allowInvalidSpan: _searchResult.NavigableItem.IsStale, CancellationToken.None).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(CancellationToken.None).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, CancellationToken.None).ConfigureAwait(false); }); } diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/AsyncSuggestedActionsSource.cs b/src/EditorFeatures/Core.Wpf/Suggestions/AsyncSuggestedActionsSource.cs index cb22c62204420..d95cb66d77b25 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/AsyncSuggestedActionsSource.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/AsyncSuggestedActionsSource.cs @@ -102,13 +102,7 @@ private async Task GetSuggestedActionsWorkerAsync( // Collectors are in priority order. So just walk them from highest to lowest. foreach (var collector in collectors) { - var priority = collector.Priority switch - { - VisualStudio.Utilities.DefaultOrderings.Highest => CodeActionRequestPriority.High, - VisualStudio.Utilities.DefaultOrderings.Default => CodeActionRequestPriority.Normal, - VisualStudio.Utilities.DefaultOrderings.Lowest => CodeActionRequestPriority.Lowest, - _ => (CodeActionRequestPriority?)null, - }; + var priority = TryGetPriority(collector.Priority); if (priority != null) { diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs index 332cf7daf6362..1b5d86630fdce 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs @@ -417,27 +417,42 @@ await InvokeBelowInputPriorityAsync(() => CodeActionOptions options, CancellationToken cancellationToken) { - if (state.Target.Owner._codeFixService != null && - state.Target.SubjectBuffer.SupportsCodeFixes()) + foreach (var order in Orderings) { - var result = await state.Target.Owner._codeFixService.GetMostSevereFixableDiagnosticAsync( - document, range.Span.ToTextSpan(), options, cancellationToken).ConfigureAwait(false); + var priority = TryGetPriority(order); + Contract.ThrowIfNull(priority); - if (result.HasFix) - { - Logger.Log(FunctionId.SuggestedActions_HasSuggestedActionsAsync); - return GetFixCategory(result.Diagnostic.Severity); - } + var result = await GetFixLevelAsync(priority.Value).ConfigureAwait(false); + if (result != null) + return result; + } - if (result.PartialResult) + return null; + + async Task GetFixLevelAsync(CodeActionRequestPriority priority) + { + if (state.Target.Owner._codeFixService != null && + state.Target.SubjectBuffer.SupportsCodeFixes()) { - // reset solution version number so that we can raise suggested action changed event - Volatile.Write(ref state.Target.LastSolutionVersionReported, InvalidSolutionVersion); - return null; + var result = await state.Target.Owner._codeFixService.GetMostSevereFixAsync( + document, range.Span.ToTextSpan(), priority, options, cancellationToken).ConfigureAwait(false); + + if (result.HasFix) + { + Logger.Log(FunctionId.SuggestedActions_HasSuggestedActionsAsync); + return GetFixCategory(result.CodeFixCollection.FirstDiagnostic.Severity); + } + + if (!result.UpToDate) + { + // reset solution version number so that we can raise suggested action changed event + Volatile.Write(ref state.Target.LastSolutionVersionReported, InvalidSolutionVersion); + return null; + } } - } - return null; + return null; + } } private async Task TryGetRefactoringSuggestedActionCategoryAsync( diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs index a99c015036d7e..25e25897204d1 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs @@ -6,18 +6,18 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.Composition; +using System.Linq; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tags; -using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; @@ -37,6 +37,11 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions [SuggestedActionPriority(DefaultOrderings.Lowest)] internal partial class SuggestedActionsSourceProvider : ISuggestedActionsSourceProvider { + public static readonly ImmutableArray Orderings = ImmutableArray.Create( + DefaultOrderings.Highest, + DefaultOrderings.Default, + DefaultOrderings.Lowest); + private static readonly Guid s_CSharpSourceGuid = new Guid("b967fea8-e2c3-4984-87d4-71a38f49e16a"); private static readonly Guid s_visualBasicSourceGuid = new Guid("4de30e93-3e0c-40c2-a4ba-1124da4539f6"); private static readonly Guid s_xamlSourceGuid = new Guid("a0572245-2eab-4c39-9f61-06a6d8c5ddda"); @@ -100,5 +105,14 @@ public SuggestedActionsSourceProvider( ? new AsyncSuggestedActionsSource(_threadingContext, _globalOptions, this, textView, textBuffer, _suggestedActionCategoryRegistry) : new SyncSuggestedActionsSource(_threadingContext, _globalOptions, this, textView, textBuffer, _suggestedActionCategoryRegistry); } + + private static CodeActionRequestPriority? TryGetPriority(string priority) + => priority switch + { + DefaultOrderings.Highest => CodeActionRequestPriority.High, + DefaultOrderings.Default => CodeActionRequestPriority.Normal, + DefaultOrderings.Lowest => CodeActionRequestPriority.Lowest, + _ => (CodeActionRequestPriority?)null, + }; } } diff --git a/src/EditorFeatures/Core/CommandHandlers/AbstractGoToCommandHandler`2.cs b/src/EditorFeatures/Core/CommandHandlers/AbstractGoToCommandHandler`2.cs index 9ea132239be22..932354b3a877b 100644 --- a/src/EditorFeatures/Core/CommandHandlers/AbstractGoToCommandHandler`2.cs +++ b/src/EditorFeatures/Core/CommandHandlers/AbstractGoToCommandHandler`2.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -194,7 +195,7 @@ private async Task ExecuteCommandWorkerAsync( definitions, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(new NavigationOptions(PreferProvisionalTab: true, ActivateTab: true), cancellationToken).ConfigureAwait(false); return; } } diff --git a/src/EditorFeatures/Core/Extensibility/NavigationBar/AbstractEditorNavigationBarItemService.cs b/src/EditorFeatures/Core/Extensibility/NavigationBar/AbstractEditorNavigationBarItemService.cs index d0a88e16c6ac0..9a7a4db15c88a 100644 --- a/src/EditorFeatures/Core/Extensibility/NavigationBar/AbstractEditorNavigationBarItemService.cs +++ b/src/EditorFeatures/Core/Extensibility/NavigationBar/AbstractEditorNavigationBarItemService.cs @@ -51,11 +51,11 @@ protected async Task NavigateToPositionAsync(Workspace workspace, DocumentId doc { var navigationService = workspace.Services.GetRequiredService(); var location = await navigationService.GetLocationForPositionAsync( - workspace, documentId, position, virtualSpace, NavigationOptions.Default, cancellationToken).ConfigureAwait(false); + workspace, documentId, position, virtualSpace, cancellationToken).ConfigureAwait(false); if (location != null) { - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, cancellationToken).ConfigureAwait(false); return; } else diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptNavigationBarItemService.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptNavigationBarItemService.cs index 5b8532e799873..112b4039c8172 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptNavigationBarItemService.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptNavigationBarItemService.cs @@ -51,9 +51,9 @@ public async Task TryNavigateToItemAsync( var workspace = document.Project.Solution.Workspace; var navigationService = workspace.Services.GetRequiredService(); var location = await navigationService.GetLocationForPositionAsync( - workspace, document.Id, navigationSpan.Start, virtualSpace: 0, NavigationOptions.Default, cancellationToken).ConfigureAwait(false); + workspace, document.Id, navigationSpan.Start, virtualSpace: 0, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, cancellationToken).ConfigureAwait(false); } public bool ShowItemGrayedIfNear(NavigationBarItem item) diff --git a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs index 3fef9772600fc..c8d2b34f606b0 100644 --- a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs +++ b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs @@ -37,8 +37,7 @@ protected AbstractAsyncGoToDefinitionService( var service = workspace.Services.GetRequiredService(); return service.GetLocationForPositionAsync( - workspace, document.Id, position, virtualSpace: 0, - new NavigationOptions(PreferProvisionalTab: true, ActivateTab: true), cancellationToken); + workspace, document.Id, position, virtualSpace: 0, cancellationToken); } public async Task FindDefinitionLocationAsync(Document document, int position, CancellationToken cancellationToken) diff --git a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs index ddc3b6fb497bf..3398a80103663 100644 --- a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs +++ b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -87,7 +88,8 @@ private async Task ExecuteCommandAsync( { var location = await asyncService.FindDefinitionLocationAsync(document, caretPosition, cancellationToken).ConfigureAwait(false); var success = location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync( + new NavigationOptions(PreferProvisionalTab: true, ActivateTab: true), cancellationToken).ConfigureAwait(false); if (success) return; diff --git a/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs b/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs index ebd76cd6226d8..8835f04ae7074 100644 --- a/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs +++ b/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs @@ -76,7 +76,6 @@ internal static class IStreamingFindUsagesPresenterExtensions definitionsBuilder.Add(item); } - var options = new NavigationOptions(PreferProvisionalTab: true, ActivateTab: true); var definitions = definitionsBuilder.ToImmutable(); // See if there's a third party external item we can navigate to. If so, defer @@ -87,8 +86,7 @@ internal static class IStreamingFindUsagesPresenterExtensions // If we're directly going to a location we need to activate the preview so // that focus follows to the new cursor position. This behavior is expected // because we are only going to navigate once successfully - var location = await item.GetNavigableLocationAsync( - workspace, options, cancellationToken).ConfigureAwait(false); + var location = await item.GetNavigableLocationAsync(workspace, cancellationToken).ConfigureAwait(false); if (location != null) return location; } @@ -103,14 +101,13 @@ internal static class IStreamingFindUsagesPresenterExtensions // There was only one location to navigate to. Just directly go to that location. If we're directly // going to a location we need to activate the preview so that focus follows to the new cursor position. - return await nonExternalItems[0].GetNavigableLocationAsync( - workspace, options, cancellationToken).ConfigureAwait(false); + return await nonExternalItems[0].GetNavigableLocationAsync(workspace, cancellationToken).ConfigureAwait(false); } if (presenter == null) return null; - return new NavigableLocation(async cancellationToken => + return new NavigableLocation(async (options, cancellationToken) => { // Can only navigate or present items on UI thread. await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); diff --git a/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionEditHandlerService.cs b/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionEditHandlerService.cs index 4d98dc3ead8aa..8851981327bc4 100644 --- a/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionEditHandlerService.cs +++ b/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionEditHandlerService.cs @@ -302,7 +302,7 @@ private async Task TryNavigateToLocationOrStartRenameSessionAsync( var location = await navigationService.GetLocationForPositionAsync( workspace, navigationOperation.DocumentId, navigationOperation.Position, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, cancellationToken).ConfigureAwait(false); return; } @@ -322,7 +322,7 @@ private async Task TryNavigateToLocationOrStartRenameSessionAsync( var location = await navigationService.GetLocationForPositionAsync( workspace, documentId, navigationToken.Value.SpanStart, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, cancellationToken).ConfigureAwait(false); return; } @@ -349,7 +349,7 @@ private async Task TryNavigateToLocationOrStartRenameSessionAsync( editorWorkspace, documentId, resolvedRenameToken.Span, cancellationToken).ConfigureAwait(false); if (location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false)) + await location.NavigateToAsync(NavigationOptions.Default, cancellationToken).ConfigureAwait(false)) { var openDocument = workspace.CurrentSolution.GetRequiredDocument(documentId); var openRoot = await openDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/Core/Implementation/ExtractInterface/AbstractExtractInterfaceCommandHandler.cs b/src/EditorFeatures/Core/Implementation/ExtractInterface/AbstractExtractInterfaceCommandHandler.cs index 68a44aaf262f4..5c03b8a0ebda5 100644 --- a/src/EditorFeatures/Core/Implementation/ExtractInterface/AbstractExtractInterfaceCommandHandler.cs +++ b/src/EditorFeatures/Core/Implementation/ExtractInterface/AbstractExtractInterfaceCommandHandler.cs @@ -82,7 +82,7 @@ public bool ExecuteCommand(ExtractInterfaceCommandArgs args, CommandExecutionCon var location = await navigationService.GetLocationForPositionAsync( workspace, result.NavigationDocumentId, 0, CancellationToken.None).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(CancellationToken.None).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, CancellationToken.None).ConfigureAwait(false); }); return true; diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Helpers.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Helpers.cs index 84bbfd329e723..6331a833faa43 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Helpers.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Helpers.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Text.Adornments; @@ -221,7 +222,7 @@ private static async Task NavigateToQuickInfoTargetAsync( var location = await GoToDefinitionHelpers.GetDefinitionLocationAsync( symbol, solution, threadingContext, streamingPresenter, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(new NavigationOptions(PreferProvisionalTab: true, ActivateTab: true), cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index d29746f7892a1..ba421e1ecddd6 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -60,7 +60,8 @@ public async Task TestGetFirstDiagnosticWithFixAsync() var reference = new MockAnalyzerReference(); var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference); var document = project.Documents.Single(); - var unused = await fixService.GetMostSevereFixableDiagnosticAsync(document, TextSpan.FromBounds(0, 0), CodeActionOptions.Default, CancellationToken.None); + var unused = await fixService.GetMostSevereFixAsync( + document, TextSpan.FromBounds(0, 0), CodeActionRequestPriority.None, CodeActionOptions.Default, CancellationToken.None); var fixer1 = (MockFixer)fixers.Single().Value; var fixer2 = (MockFixer)reference.Fixer!; @@ -297,7 +298,8 @@ private static async Task GetFirstDiagnosticWithFixWithExceptionValidationAsync( errorReportingService.OnError = message => errorReported = true; GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager); - var unused = await tuple.codeFixService.GetMostSevereFixableDiagnosticAsync(document, TextSpan.FromBounds(0, 0), CodeActionOptions.Default, CancellationToken.None); + var unused = await tuple.codeFixService.GetMostSevereFixAsync( + document, TextSpan.FromBounds(0, 0), CodeActionRequestPriority.None, CodeActionOptions.Default, CancellationToken.None); Assert.True(extensionManager.IsDisabled(codefix)); Assert.False(extensionManager.IsIgnored(codefix)); Assert.True(errorReported); diff --git a/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs index 5926e8873d0ca..c652fbf59aa6d 100644 --- a/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/Test/EditAndContinue/Helpers/MockDiagnosticAnalyzerService.cs @@ -50,7 +50,7 @@ public Task> GetProjectDiagnosticsForIdsAsync(Sol public Task> GetSpecificCachedDiagnosticsAsync(Workspace workspace, object id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public Task TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, ArrayBuilder diagnostics, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) + public Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync(Document document, TextSpan range, Func? shouldIncludeDiagnostic, bool includeSuppressedDiagnostics = false, CodeActionRequestPriority priority = CodeActionRequestPriority.None, CancellationToken cancellationToken = default) => throw new NotImplementedException(); } } diff --git a/src/EditorFeatures/Test/SuggestedActions/SuggestedActionSourceProviderTests.cs b/src/EditorFeatures/Test/SuggestedActions/SuggestedActionSourceProviderTests.cs new file mode 100644 index 0000000000000..84806cc7953e2 --- /dev/null +++ b/src/EditorFeatures/Test/SuggestedActions/SuggestedActionSourceProviderTests.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; +using Microsoft.VisualStudio.Language.Intellisense; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.SuggestedActions +{ + public class SuggestedActionSourceProviderTests + { + [Fact] + public void EnsureAttributesMatchData() + { + // Ensure that the list of orderings on this type matches the set we expose in SuggestedActionsSourceProvider.Orderings + var attributes = typeof(SuggestedActionsSourceProvider).GetCustomAttributes(inherit: false) + .OfType() + .ToImmutableArray(); + AssertEx.SetEqual(attributes.Select(a => a.Priority), SuggestedActionsSourceProvider.Orderings); + } + } +} diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index e565227170f1e..8bcb00f82195c 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -2223,16 +2223,14 @@ class C Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) ' Verify diagnostics for span - Dim diagnostics As New PooledObjects.ArrayBuilder(Of DiagnosticData) - Await diagnosticService.TryAppendDiagnosticsForSpanAsync(document, span, diagnostics) - Dim diagnostic = Assert.Single(diagnostics) + Dim t = Await diagnosticService.TryGetDiagnosticsForSpanAsync(document, span, shouldIncludeDiagnostic:=Nothing) + Dim diagnostic = Assert.Single(t.diagnostics) Assert.Equal("CS0219", diagnostic.Id) ' Verify no diagnostics outside the local decl span span = localDecl.GetLastToken().GetNextToken().GetNextToken().Span - diagnostics.Clear() - Await diagnosticService.TryAppendDiagnosticsForSpanAsync(document, span, diagnostics) - Assert.Empty(diagnostics) + t = Await diagnosticService.TryGetDiagnosticsForSpanAsync(document, span, shouldIncludeDiagnostic:=Nothing) + Assert.Empty(t.diagnostics) End Using End Function @@ -2322,8 +2320,7 @@ class MyClass Assert.Equal(analyzer.Descriptor.Id, descriptors.Single().Id) ' Try get diagnostics for span - Dim diagnostics As New PooledObjects.ArrayBuilder(Of DiagnosticData) - Await diagnosticService.TryAppendDiagnosticsForSpanAsync(document, span, diagnostics) + Await diagnosticService.TryGetDiagnosticsForSpanAsync(document, span, shouldIncludeDiagnostic:=Nothing) ' Verify only span-based analyzer is invoked with TryAppendDiagnosticsForSpanAsync Assert.Equal(isSpanBasedAnalyzer, analyzer.ReceivedOperationCallback) diff --git a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTestsBase.vb b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTestsBase.vb index 081e0baf1c18b..f541adf3e091a 100644 --- a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTestsBase.vb +++ b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTestsBase.vb @@ -46,7 +46,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition Dim defLocation = Await goToDefService.FindDefinitionLocationAsync(document, cursorPosition, CancellationToken.None) Dim actualResult = defLocation IsNot Nothing AndAlso - Await defLocation.NavigateToAsync(CancellationToken.None) + Await defLocation.NavigateToAsync(NavigationOptions.Default, CancellationToken.None) Assert.Equal(expectedResult, actualResult) Dim expectedLocations As New List(Of FilePathAndSpan) diff --git a/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/MockDocumentNavigationService.vb b/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/MockDocumentNavigationService.vb index ec1210712e59a..40043278d64ac 100644 --- a/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/MockDocumentNavigationService.vb +++ b/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/MockDocumentNavigationService.vb @@ -21,7 +21,6 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers Public _triedNavigationToSpan As Boolean Public _documentId As DocumentId - Public _options As NavigationOptions Public _line As Integer = -1 Public _offset As Integer = -1 Public _span As TextSpan = Nothing @@ -40,30 +39,27 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers Return If(_canNavigateToSpan, SpecializedTasks.True, SpecializedTasks.False) End Function - Public Function GetLocationForLineAndOffsetAsync(workspace As Workspace, documentId As DocumentId, lineNumber As Integer, offset As Integer, options As NavigationOptions, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForLineAndOffsetAsync + Public Function GetLocationForLineAndOffsetAsync(workspace As Workspace, documentId As DocumentId, lineNumber As Integer, offset As Integer, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForLineAndOffsetAsync _triedNavigationToLineAndOffset = True _documentId = documentId - _options = options _line = lineNumber _offset = offset Return NavigableLocation.TestAccessor.Create(_canNavigateToLineAndOffset) End Function - Public Function GetLocationForPositionAsync(workspace As Workspace, documentId As DocumentId, position As Integer, virtualSpace As Integer, options As NavigationOptions, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForPositionAsync + Public Function GetLocationForPositionAsync(workspace As Workspace, documentId As DocumentId, position As Integer, virtualSpace As Integer, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForPositionAsync _triedNavigationToPosition = True _documentId = documentId - _options = options _position = position _positionVirtualSpace = virtualSpace Return NavigableLocation.TestAccessor.Create(_canNavigateToPosition) End Function - Public Function GetLocationForSpanAsync(workspace As Workspace, documentId As DocumentId, textSpan As TextSpan, options As NavigationOptions, allowInvalidSpan As Boolean, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForSpanAsync + Public Function GetLocationForSpanAsync(workspace As Workspace, documentId As DocumentId, textSpan As TextSpan, allowInvalidSpan As Boolean, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForSpanAsync _triedNavigationToSpan = True _documentId = documentId - _options = options _span = textSpan Return NavigableLocation.TestAccessor.Create(_canNavigateToSpan) diff --git a/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/MockSymbolNavigationService.vb b/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/MockSymbolNavigationService.vb index f67968c1d517a..0f429e4d58034 100644 --- a/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/MockSymbolNavigationService.vb +++ b/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/MockSymbolNavigationService.vb @@ -16,7 +16,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers Public _triedSymbolNavigationNotify As Boolean Public _wouldNavigateToSymbol As Boolean - Public Function GetNavigableLocationAsync(symbol As ISymbol, project As Project, options As NavigationOptions, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements ISymbolNavigationService.GetNavigableLocationAsync + Public Function GetNavigableLocationAsync(symbol As ISymbol, project As Project, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements ISymbolNavigationService.GetNavigableLocationAsync _triedNavigationToSymbol = True Return NavigableLocation.TestAccessor.Create(True) End Function diff --git a/src/EditorFeatures/TestUtilities2/Utilities/MockDocumentNavigationServiceProvider.vb b/src/EditorFeatures/TestUtilities2/Utilities/MockDocumentNavigationServiceProvider.vb index c9ee56af917c9..923f5b009272a 100644 --- a/src/EditorFeatures/TestUtilities2/Utilities/MockDocumentNavigationServiceProvider.vb +++ b/src/EditorFeatures/TestUtilities2/Utilities/MockDocumentNavigationServiceProvider.vb @@ -7,7 +7,6 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Navigation -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Imports Roslyn.Utilities @@ -38,7 +37,6 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities Public ProvidedOffset As Integer Public ProvidedPosition As Integer Public ProvidedVirtualSpace As Integer - Public ProvidedOptions As NavigationOptions Public CanNavigateToLineAndOffsetReturnValue As Boolean = True Public CanNavigateToPositionReturnValue As Boolean = True @@ -70,28 +68,25 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities Return If(CanNavigateToSpanReturnValue, SpecializedTasks.True, SpecializedTasks.False) End Function - Public Function GetLocationForLineAndOffsetAsync(workspace As Workspace, documentId As DocumentId, lineNumber As Integer, offset As Integer, options As NavigationOptions, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForLineAndOffsetAsync + Public Function GetLocationForLineAndOffsetAsync(workspace As Workspace, documentId As DocumentId, lineNumber As Integer, offset As Integer, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForLineAndOffsetAsync Me.ProvidedDocumentId = documentId Me.ProvidedLineNumber = lineNumber Me.ProvidedOffset = offset - Me.ProvidedOptions = options Return NavigableLocation.TestAccessor.Create(TryNavigateToLineAndOffsetReturnValue) End Function - Public Function GetLocationForPositionAsync(workspace As Workspace, documentId As DocumentId, position As Integer, virtualSpace As Integer, options As NavigationOptions, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForPositionAsync + Public Function GetLocationForPositionAsync(workspace As Workspace, documentId As DocumentId, position As Integer, virtualSpace As Integer, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForPositionAsync Me.ProvidedDocumentId = documentId Me.ProvidedPosition = position Me.ProvidedVirtualSpace = virtualSpace - Me.ProvidedOptions = options Return NavigableLocation.TestAccessor.Create(TryNavigateToPositionReturnValue) End Function - Public Function GetLocationForSpanAsync(workspace As Workspace, documentId As DocumentId, textSpan As TextSpan, options As NavigationOptions, allowInvalidSpans As Boolean, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForSpanAsync + Public Function GetLocationForSpanAsync(workspace As Workspace, documentId As DocumentId, textSpan As TextSpan, allowInvalidSpans As Boolean, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements IDocumentNavigationService.GetLocationForSpanAsync Me.ProvidedDocumentId = documentId Me.ProvidedTextSpan = textSpan - Me.ProvidedOptions = options Return NavigableLocation.TestAccessor.Create(TryNavigateToSpanReturnValue) End Function diff --git a/src/EditorFeatures/TestUtilities2/Utilities/MockSymbolNavigationServiceProvider.vb b/src/EditorFeatures/TestUtilities2/Utilities/MockSymbolNavigationServiceProvider.vb index 960fe94d16fa5..50d005dfc5d5b 100644 --- a/src/EditorFeatures/TestUtilities2/Utilities/MockSymbolNavigationServiceProvider.vb +++ b/src/EditorFeatures/TestUtilities2/Utilities/MockSymbolNavigationServiceProvider.vb @@ -35,7 +35,6 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities Public TryNavigateToSymbolProvidedSymbol As ISymbol Public TryNavigateToSymbolProvidedProject As Project - Public TryNavigateToSymbolProvidedOptions As NavigationOptions Public TrySymbolNavigationNotifyProvidedSymbol As ISymbol Public TrySymbolNavigationNotifyProvidedProject As Project @@ -46,10 +45,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities Public NavigationLineNumberReturnValue As Integer Public NavigationCharOffsetReturnValue As Integer - Public Function GetNavigableLocationAsync(symbol As ISymbol, project As Project, options As NavigationOptions, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements ISymbolNavigationService.GetNavigableLocationAsync + Public Function GetNavigableLocationAsync(symbol As ISymbol, project As Project, cancellationToken As CancellationToken) As Task(Of INavigableLocation) Implements ISymbolNavigationService.GetNavigableLocationAsync Me.TryNavigateToSymbolProvidedSymbol = symbol Me.TryNavigateToSymbolProvidedProject = project - Me.TryNavigateToSymbolProvidedOptions = options Return NavigableLocation.TestAccessor.Create(True) End Function diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 401517ac5ebe0..12ce0be23aaa1 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Configuration; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -34,9 +35,6 @@ namespace Microsoft.CodeAnalysis.CodeFixes [Export(typeof(ICodeFixService)), Shared] internal partial class CodeFixService : ICodeFixService { - private static readonly Comparison s_diagnosticDataComparisonById = - new((d1, d2) => DiagnosticId.CompareOrdinal(d1.Id, d2.Id)); - private readonly IDiagnosticAnalyzerService _diagnosticService; private readonly ImmutableArray> _fixers; private readonly ImmutableDictionary>> _fixersPerLanguageMap; @@ -73,73 +71,83 @@ public CodeFixService( _configurationProvidersMap = GetConfigurationProvidersPerLanguageMap(configurationProviders); } - public async Task GetMostSevereFixableDiagnosticAsync( - Document document, TextSpan range, CodeActionOptions options, CancellationToken cancellationToken) + private Func? GetShouldIncludeDiagnosticPredicate( + Document document, + CodeActionRequestPriority priority) { - cancellationToken.ThrowIfCancellationRequested(); + // For Normal or Low priority, we only need to execute analyzers which can report at least one fixable + // diagnostic that can have a non-suppression/configuration fix. + // + // For CodeActionPriorityRequest.High, we only run compiler analyzer, which always has fixable diagnostics, + // so we can return a null predicate here to include all diagnostics. + + if (!(priority is CodeActionRequestPriority.Normal or CodeActionRequestPriority.Low)) + return null; + + var hasWorkspaceFixers = TryGetWorkspaceFixersMap(document, out var workspaceFixersMap); + var projectFixersMap = GetProjectFixers(document.Project); - if (!document.IsOpen()) + return id => { - return default; - } + if (hasWorkspaceFixers && workspaceFixersMap!.Value.ContainsKey(id)) + return true; - using var _ = ArrayBuilder.GetInstance(out var diagnostics); - using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + return projectFixersMap.ContainsKey(id); + }; + } + + public async Task GetMostSevereFixAsync( + Document document, TextSpan range, CodeActionRequestPriority priority, CodeActionOptions options, CancellationToken cancellationToken) + { + var (allDiagnostics, upToDate) = await _diagnosticService.TryGetDiagnosticsForSpanAsync( + document, range, GetShouldIncludeDiagnosticPredicate(document, priority), + includeSuppressedDiagnostics: false, priority, cancellationToken).ConfigureAwait(false); + + var spanToDiagnostics = ConvertToMap(allDiagnostics); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var linkedToken = linkedTokenSource.Token; - // This flag is used by SuggestedActionsSource to track what solution is was - // last able to get "full results" for. - var isFullResult = await _diagnosticService.TryAppendDiagnosticsForSpanAsync( - document, range, diagnostics, cancellationToken: linkedToken).ConfigureAwait(false); + var spanToErrorDiagnostics = new SortedDictionary>(); + var spanToOtherDiagnostics = new SortedDictionary>(); - var errorDiagnostics = diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error); - var otherDiagnostics = diagnostics.Where(d => d.Severity != DiagnosticSeverity.Error); + foreach (var (span, diagnostics) in spanToDiagnostics) + { + foreach (var diagnostic in diagnostics) + { + var preferredMap = diagnostic.Severity == DiagnosticSeverity.Error + ? spanToErrorDiagnostics + : spanToOtherDiagnostics; - // Kick off a task that will determine there's an Error Diagnostic with a fixer - var errorDiagnosticsTask = Task.Run( - () => GetFirstDiagnosticWithFixAsync(document, errorDiagnostics, range, options, linkedToken), - linkedToken); + preferredMap.MultiAdd(span, diagnostic); + } + } - // Kick off a task that will determine if any non-Error Diagnostic has a fixer - var otherDiagnosticsTask = Task.Run( - () => GetFirstDiagnosticWithFixAsync(document, otherDiagnostics, range, options, linkedToken), - linkedToken); + var errorFixTask = Task.Run(() => GetFirstFixAsync(spanToErrorDiagnostics, cancellationToken), cancellationToken); + var otherFixTask = Task.Run(() => GetFirstFixAsync(spanToOtherDiagnostics, linkedToken), linkedToken); // If the error diagnostics task happens to complete with a non-null result before // the other diagnostics task, we can cancel the other task. - var diagnostic = await errorDiagnosticsTask.ConfigureAwait(false) - ?? await otherDiagnosticsTask.ConfigureAwait(false); + var collection = await errorFixTask.ConfigureAwait(false) ?? + await otherFixTask.ConfigureAwait(false); linkedTokenSource.Cancel(); - return new FirstDiagnosticResult(partialResult: !isFullResult, - hasFix: diagnostic != null, - diagnostic: diagnostic); - } - - private async Task GetFirstDiagnosticWithFixAsync( - Document document, - IEnumerable severityGroup, - TextSpan range, - CodeActionOptions options, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + return new FirstFixResult(upToDate, collection); - foreach (var diagnostic in severityGroup) + async Task GetFirstFixAsync( + SortedDictionary> spanToDiagnostics, + CancellationToken cancellationToken) { - if (!range.IntersectsWith(diagnostic.GetTextSpan())) + await foreach (var collection in StreamFixesAsync( + document, spanToDiagnostics, fixAllForInSpan: false, + priority, options, _ => null, cancellationToken).ConfigureAwait(false)) { - continue; + // Stop at the result error we see. + return collection; } - if (await ContainsAnyFixAsync(document, diagnostic, options, cancellationToken).ConfigureAwait(false)) - { - return diagnostic; - } + return null; } - - return null; } public async IAsyncEnumerable StreamFixesAsync( @@ -150,57 +158,32 @@ public async IAsyncEnumerable StreamFixesAsync( Func addOperationScope, [EnumeratorCancellation] CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); - - // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic service to give back - // current diagnostics for the given span, and it will use that to get fixes. internally diagnostic service will either return cached information - // (if it is up-to-date) or synchronously do the work at the spot. - // - // this design's weakness is that each side don't have enough information to narrow down works to do. it will most likely always do more works than needed. - // sometimes way more than it is needed. (compilation) - - // group diagnostics by their diagnostics span - // invariant: later code gathers & runs CodeFixProviders for diagnostics with one identical diagnostics span (that gets set later as CodeFixCollection's TextSpan) - // order diagnostics by span. - var aggregatedDiagnostics = new SortedDictionary>(); - - // For 'CodeActionPriorityRequest.Normal' or 'CodeActionPriorityRequest.Low', we do not compute suppression/configuration fixes, - // those fixes have a dedicated request priority 'CodeActionPriorityRequest.Lowest'. - // Hence, for Normal or Low priority, we only need to execute analyzers which can report at least one fixable diagnostic - // that can have a non-suppression/configuration fix. - // Note that for 'CodeActionPriorityRequest.High', we only run compiler analyzer, which always has fixable diagnostics, - // so we can pass in null. - var shouldIncludeDiagnostic = priority is CodeActionRequestPriority.Normal or CodeActionRequestPriority.Low - ? GetFixableDiagnosticFilter(document) - : null; - // We only need to compute suppression/configuration fixes when request priority is // 'CodeActionPriorityRequest.Lowest' or 'CodeActionPriorityRequest.None'. var includeSuppressionFixes = priority is CodeActionRequestPriority.Lowest or CodeActionRequestPriority.None; - var diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync( - document, range, shouldIncludeDiagnostic, includeSuppressionFixes, priority, addOperationScope, cancellationToken).ConfigureAwait(false); - foreach (var diagnostic in diagnostics) - { - if (diagnostic.IsSuppressed) - continue; + // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic + // service to give back current diagnostics for the given span, and it will use that to get fixes. + // internally diagnostic service will either return cached information (if it is up-to-date) or + // synchronously do the work at the spot. + // + // this design's weakness is that each side don't have enough information to narrow down works to do. it + // will most likely always do more works than needed. sometimes way more than it is needed. (compilation) - var list = aggregatedDiagnostics.GetOrAdd(diagnostic.GetTextSpan(), static _ => new List()); - list.Add(diagnostic); - } + var diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync( + document, range, GetShouldIncludeDiagnosticPredicate(document, priority), + includeSuppressionFixes, priority, addOperationScope, cancellationToken).ConfigureAwait(false); - if (aggregatedDiagnostics.Count == 0) + if (diagnostics.IsEmpty) yield break; - // Order diagnostics by DiagnosticId so the fixes are in a deterministic order. - foreach (var (_, diagnosticList) in aggregatedDiagnostics) - diagnosticList.Sort(s_diagnosticDataComparisonById); + var spanToDiagnostics = ConvertToMap(diagnostics); // 'CodeActionRequestPriority.Lowest' is used when the client only wants suppression/configuration fixes. if (priority != CodeActionRequestPriority.Lowest) { await foreach (var collection in StreamFixesAsync( - document, aggregatedDiagnostics, fixAllForInSpan: false, + document, spanToDiagnostics, fixAllForInSpan: false, priority, options, addOperationScope, cancellationToken).ConfigureAwait(false)) { yield return collection; @@ -212,7 +195,7 @@ public async IAsyncEnumerable StreamFixesAsync( { // Ensure that we do not register duplicate configuration fixes. using var _2 = PooledHashSet.GetInstance(out var registeredConfigurationFixTitles); - foreach (var (span, diagnosticList) in aggregatedDiagnostics) + foreach (var (span, diagnosticList) in spanToDiagnostics) { await foreach (var codeFixCollection in StreamConfigurationFixesAsync( document, span, diagnosticList, registeredConfigurationFixTitles, options, cancellationToken).ConfigureAwait(false)) @@ -221,23 +204,29 @@ public async IAsyncEnumerable StreamFixesAsync( } } } + } - yield break; - - // Local functions - Func GetFixableDiagnosticFilter(Document document) + private static SortedDictionary> ConvertToMap( + ImmutableArray diagnostics) + { + // group diagnostics by their diagnostics span + // + // invariant: later code gathers & runs CodeFixProviders for diagnostics with one identical diagnostics span + // (that gets set later as CodeFixCollection's TextSpan) order diagnostics by span. + var spanToDiagnostics = new SortedDictionary>(); + foreach (var diagnostic in diagnostics) { - var hasWorkspaceFixers = TryGetWorkspaceFixersMap(document, out var workspaceFixersMap); - var projectFixersMap = GetProjectFixers(document.Project); - - return id => - { - if (hasWorkspaceFixers && workspaceFixersMap!.Value.ContainsKey(id)) - return true; + if (diagnostic.IsSuppressed) + continue; - return projectFixersMap.ContainsKey(id); - }; + spanToDiagnostics.MultiAdd(diagnostic.GetTextSpan(), diagnostic); } + + // Order diagnostics by DiagnosticId so the fixes are in a deterministic order. + foreach (var (_, diagnosticList) in spanToDiagnostics) + diagnosticList.Sort(static (d1, d2) => DiagnosticId.CompareOrdinal(d1.Id, d2.Id)); + + return spanToDiagnostics; } public async Task GetDocumentFixAllForIdInSpanAsync( @@ -494,7 +483,7 @@ void AddAllFixers( continue; allFixers.Add(fixer); - fixerToRangesAndDiagnostics.GetOrAdd(fixer, static _ => new()).Add((range, diagnostics)); + fixerToRangesAndDiagnostics.MultiAdd(fixer, (range, diagnostics)); } } } @@ -779,83 +768,6 @@ private async Task> GetProjectDiagnosticsAsync(Project p } } - private async Task ContainsAnyFixAsync( - Document document, DiagnosticData diagnosticData, CodeActionOptions options, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var workspaceFixers = ImmutableArray.Empty; - var hasAnySharedFixer = TryGetWorkspaceFixersMap(document, out var fixerMap) && fixerMap.Value.TryGetValue(diagnosticData.Id, out workspaceFixers); - var hasAnyProjectFixer = GetProjectFixers(document.Project).TryGetValue(diagnosticData.Id, out var projectFixers); - - // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive - if (hasAnySharedFixer && document.Project.Solution.Workspace.Kind == WorkspaceKind.Interactive) - { - workspaceFixers = workspaceFixers.WhereAsArray(IsInteractiveCodeFixProvider); - hasAnySharedFixer = workspaceFixers.Any(); - } - - var hasConfigurationFixer = - _configurationProvidersMap.TryGetValue(document.Project.Language, out var lazyConfigurationProviders) && - !lazyConfigurationProviders.Value.IsDefaultOrEmpty; - - if (!hasAnySharedFixer && !hasAnyProjectFixer && !hasConfigurationFixer) - { - return false; - } - - var allFixers = ImmutableArray.Empty; - if (hasAnySharedFixer) - { - allFixers = workspaceFixers; - } - - if (hasAnyProjectFixer) - { - allFixers = allFixers.AddRange(projectFixers!); - } - - var diagnostic = await diagnosticData.ToDiagnosticAsync(document.Project, cancellationToken).ConfigureAwait(false); - - if (hasConfigurationFixer) - { - foreach (var lazyConfigurationProvider in lazyConfigurationProviders!.Value) - { - if (lazyConfigurationProvider.IsFixableDiagnostic(diagnostic)) - { - return true; - } - } - } - - var fixes = new List(); - var context = new CodeFixContext(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), - - // TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? - (action, applicableDiagnostics) => - { - // Serialize access for thread safety - we don't know what thread the fix provider will call this delegate from. - lock (fixes) - { - fixes.Add(new CodeFix(document.Project, action, applicableDiagnostics)); - } - }, - options, - cancellationToken); - - var extensionManager = document.Project.Solution.Workspace.Services.GetRequiredService(); - - // we do have fixer. now let's see whether it actually can fix it - foreach (var fixer in allFixers) - { - await extensionManager.PerformActionAsync(fixer, () => fixer.RegisterCodeFixesAsync(context) ?? Task.CompletedTask).ConfigureAwait(false); - if (fixes.Count > 0) - return true; - } - - return false; - } - private bool IsInteractiveCodeFixProvider(CodeFixProvider provider) { // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive diff --git a/src/Features/Core/Portable/CodeFixes/FirstDiagnosticResult.cs b/src/Features/Core/Portable/CodeFixes/FirstDiagnosticResult.cs deleted file mode 100644 index eab1bc7048bc8..0000000000000 --- a/src/Features/Core/Portable/CodeFixes/FirstDiagnosticResult.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Microsoft.CodeAnalysis.CodeFixes -{ - internal readonly struct FirstDiagnosticResult - { - public readonly bool PartialResult; - public readonly bool HasFix; - public readonly DiagnosticData Diagnostic; - - public FirstDiagnosticResult(bool partialResult, bool hasFix, DiagnosticData diagnostic) - { - PartialResult = partialResult; - HasFix = hasFix; - Diagnostic = diagnostic; - } - } -} diff --git a/src/Features/Core/Portable/CodeFixes/FirstFixResult.cs b/src/Features/Core/Portable/CodeFixes/FirstFixResult.cs new file mode 100644 index 0000000000000..83674aa0355f8 --- /dev/null +++ b/src/Features/Core/Portable/CodeFixes/FirstFixResult.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.CodeAnalysis.CodeFixes +{ + internal readonly struct FirstFixResult + { + public readonly bool UpToDate; + public readonly CodeFixCollection? CodeFixCollection; + + [MemberNotNullWhen(true, nameof(CodeFixCollection))] + public bool HasFix => CodeFixCollection != null; + + public FirstFixResult(bool upToDate, CodeFixCollection? codeFixCollection) + { + UpToDate = upToDate; + CodeFixCollection = codeFixCollection; + } + } +} diff --git a/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs b/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs index 1f2109bc9fe6c..a23d3675febdd 100644 --- a/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs @@ -17,10 +17,17 @@ namespace Microsoft.CodeAnalysis.CodeFixes internal interface ICodeFixService { IAsyncEnumerable StreamFixesAsync(Document document, TextSpan textSpan, CodeActionRequestPriority priority, CodeActionOptions options, Func addOperationScope, CancellationToken cancellationToken); + + /// + /// Similar to except that instead of streaming all results, this ends with the + /// first. This will also attempt to return a fix for an error first, but will fall back to any fix if that + /// does not succeed. + /// + Task GetMostSevereFixAsync(Document document, TextSpan range, CodeActionRequestPriority priority, CodeActionOptions options, CancellationToken cancellationToken); + Task GetDocumentFixAllForIdInSpanAsync(Document document, TextSpan textSpan, string diagnosticId, CodeActionOptions options, CancellationToken cancellationToken); Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document document, string diagnosticId, IProgressTracker progressTracker, CodeActionOptions options, CancellationToken cancellationToken); CodeFixProvider? GetSuppressionFixer(string language, IEnumerable diagnosticIds); - Task GetMostSevereFixableDiagnosticAsync(Document document, TextSpan range, CodeActionOptions options, CancellationToken cancellationToken); } internal static class ICodeFixServiceExtensions diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs index 0167fa8aa2ab9..6cd64ed6fb3d1 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs @@ -64,16 +64,29 @@ public void Reanalyze(Workspace workspace, IEnumerable? projectIds = } } - public Task TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, ArrayBuilder diagnostics, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) + public Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( + Document document, + TextSpan range, + Func? shouldIncludeDiagnostic, + bool includeSuppressedDiagnostics = false, + CodeActionRequestPriority priority = CodeActionRequestPriority.None, + CancellationToken cancellationToken = default) { if (_map.TryGetValue(document.Project.Solution.Workspace, out var analyzer)) { // always make sure that analyzer is called on background thread. - return Task.Run(() => analyzer.TryAppendDiagnosticsForSpanAsync( - document, range, diagnostics, shouldIncludeDiagnostic: null, includeSuppressedDiagnostics, CodeActionRequestPriority.None, blockForData: false, addOperationScope: null, cancellationToken), cancellationToken); + return Task.Run(async () => + { + using var _ = ArrayBuilder.GetInstance(out var diagnostics); + var upToDate = await analyzer.TryAppendDiagnosticsForSpanAsync( + document, range, diagnostics, shouldIncludeDiagnostic, + includeSuppressedDiagnostics, priority, blockForData: false, + addOperationScope: null, cancellationToken).ConfigureAwait(false); + return (diagnostics.ToImmutable(), upToDate); + }, cancellationToken); } - return SpecializedTasks.False; + return Task.FromResult((ImmutableArray.Empty, upToDate: false)); } public Task> GetDiagnosticsForSpanAsync( diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index ae6dff7a009ee..b25c464875796 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Diagnostics @@ -75,7 +74,7 @@ internal interface IDiagnosticAnalyzerService /// Use /// if you want to force complete all analyzers and get up-to-date diagnostics for all analyzers for the given span. /// - Task TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, ArrayBuilder diagnostics, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default); + Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync(Document document, TextSpan range, Func? shouldIncludeDiagnostic, bool includeSuppressedDiagnostics = false, CodeActionRequestPriority priority = CodeActionRequestPriority.None, CancellationToken cancellationToken = default); /// /// Return up to date diagnostics for the given span for the document diff --git a/src/Features/Core/Portable/DocumentSpanExtensions.cs b/src/Features/Core/Portable/DocumentSpanExtensions.cs index ed54deaed1c78..4444f00008ebd 100644 --- a/src/Features/Core/Portable/DocumentSpanExtensions.cs +++ b/src/Features/Core/Portable/DocumentSpanExtensions.cs @@ -26,10 +26,10 @@ private static (Workspace workspace, IDocumentNavigationService service) GetNavi return (workspace, service); } - public static Task GetNavigableLocationAsync(this DocumentSpan documentSpan, NavigationOptions options, CancellationToken cancellationToken) + public static Task GetNavigableLocationAsync(this DocumentSpan documentSpan, CancellationToken cancellationToken) { var (workspace, service) = GetNavigationParts(documentSpan); - return service.GetLocationForSpanAsync(workspace, documentSpan.Document.Id, documentSpan.SourceSpan, options, allowInvalidSpan: false, cancellationToken); + return service.GetLocationForSpanAsync(workspace, documentSpan.Document.Id, documentSpan.SourceSpan, allowInvalidSpan: false, cancellationToken); } public static async Task IsHiddenAsync( diff --git a/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeEmbeddedLanguage.cs b/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeEmbeddedLanguage.cs index 2993b0877c06b..700f327f9550d 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeEmbeddedLanguage.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeEmbeddedLanguage.cs @@ -17,7 +17,7 @@ internal class DateAndTimeEmbeddedLanguage : IEmbeddedLanguage public readonly EmbeddedLanguageInfo Info; // We don't currently expose a classifier for Date/Time literals. However, one could always be added in the future. - public ISyntaxClassifier? Classifier => null; + public IEmbeddedLanguageClassifier? Classifier => null; public DateAndTimeEmbeddedLanguage(EmbeddedLanguageInfo info) { diff --git a/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedLanguage.cs b/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedLanguage.cs index 8514e1b965138..fd6640a22ded7 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedLanguage.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedLanguage.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Features.EmbeddedLanguages.Json.LanguageService { internal class JsonEmbeddedLanguage : IEmbeddedLanguageFeatures { - public ISyntaxClassifier Classifier { get; } + public IEmbeddedLanguageClassifier? Classifier { get; } // No document-highlights for embedded json currently. public IDocumentHighlightsService? DocumentHighlightsService => null; @@ -21,7 +21,7 @@ internal class JsonEmbeddedLanguage : IEmbeddedLanguageFeatures public JsonEmbeddedLanguage(EmbeddedLanguageInfo info) { - Classifier = new JsonEmbeddedClassifier(info); + Classifier = new JsonEmbeddedLanguageClassifier(info); } } } diff --git a/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedClassifier.cs b/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedLanguageClassifier.cs similarity index 78% rename from src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedClassifier.cs rename to src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedLanguageClassifier.cs index ea29440eedc04..90f0e2671f836 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedClassifier.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonEmbeddedLanguageClassifier.cs @@ -2,7 +2,6 @@ // 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 Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.EmbeddedLanguages.Common; using Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices; @@ -10,8 +9,6 @@ namespace Microsoft.CodeAnalysis.Features.EmbeddedLanguages.Json.LanguageServices { - using System.Collections.Immutable; - using Microsoft.CodeAnalysis.Classification.Classifiers; using static EmbeddedSyntaxHelpers; using JsonToken = EmbeddedSyntaxToken; @@ -20,54 +17,48 @@ namespace Microsoft.CodeAnalysis.Features.EmbeddedLanguages.Json.LanguageService /// /// Classifier impl for embedded json strings. /// - internal class JsonEmbeddedClassifier : AbstractSyntaxClassifier + internal class JsonEmbeddedLanguageClassifier : IEmbeddedLanguageClassifier { private static readonly ObjectPool s_visitorPool = new(() => new Visitor()); private readonly EmbeddedLanguageInfo _info; - public override ImmutableArray SyntaxTokenKinds { get; } - - public JsonEmbeddedClassifier(EmbeddedLanguageInfo info) + public JsonEmbeddedLanguageClassifier(EmbeddedLanguageInfo info) { _info = info; - SyntaxTokenKinds = info.AllStringLiteralKinds; } - public override void AddClassifications( - SyntaxToken token, - SemanticModel semanticModel, - ClassificationOptions options, - ArrayBuilder result, - CancellationToken cancellationToken) + public void RegisterClassifications(EmbeddedLanguageClassifierContext context) { + var token = context.SyntaxToken; if (!_info.IsAnyStringLiteral(token.RawKind)) return; - if (!options.ColorizeJsonPatterns) + if (!context.Options.ColorizeJsonPatterns) return; + var semanticModel = context.SemanticModel; var detector = JsonLanguageDetector.GetOrCreate(semanticModel.Compilation, _info); // We do support json classification in strings that look very likely to be json, even if we aren't 100% // certain if it truly is json. - var tree = detector.TryParseString(token, semanticModel, includeProbableStrings: true, cancellationToken); + var tree = detector.TryParseString(token, semanticModel, includeProbableStrings: true, context.CancellationToken); if (tree == null) return; var visitor = s_visitorPool.Allocate(); try { - visitor.Result = result; - AddClassifications(tree.Root, visitor, result); + visitor.Context = context; + AddClassifications(tree.Root, visitor, context); } finally { - visitor.Result = null; + visitor.Context = default; s_visitorPool.Free(visitor); } } - private static void AddClassifications(JsonNode node, Visitor visitor, ArrayBuilder result) + private static void AddClassifications(JsonNode node, Visitor visitor, EmbeddedLanguageClassifierContext context) { node.Accept(visitor); @@ -75,42 +66,41 @@ private static void AddClassifications(JsonNode node, Visitor visitor, ArrayBuil { if (child.IsNode) { - AddClassifications(child.Node, visitor, result); + AddClassifications(child.Node, visitor, context); } else { - AddTriviaClassifications(child.Token, result); + AddTriviaClassifications(child.Token, context); } } } - private static void AddTriviaClassifications(JsonToken token, ArrayBuilder result) + private static void AddTriviaClassifications(JsonToken token, EmbeddedLanguageClassifierContext context) { foreach (var trivia in token.LeadingTrivia) - AddTriviaClassifications(trivia, result); + AddTriviaClassifications(trivia, context); foreach (var trivia in token.TrailingTrivia) - AddTriviaClassifications(trivia, result); + AddTriviaClassifications(trivia, context); } - private static void AddTriviaClassifications(JsonTrivia trivia, ArrayBuilder result) + private static void AddTriviaClassifications(JsonTrivia trivia, EmbeddedLanguageClassifierContext context) { if (trivia.Kind is JsonKind.MultiLineCommentTrivia or JsonKind.SingleLineCommentTrivia && trivia.VirtualChars.Length > 0) { - result.Add(new ClassifiedSpan( - ClassificationTypeNames.JsonComment, GetSpan(trivia.VirtualChars))); + context.AddClassification(ClassificationTypeNames.JsonComment, GetSpan(trivia.VirtualChars)); } } private class Visitor : IJsonNodeVisitor { - public ArrayBuilder? Result; + public EmbeddedLanguageClassifierContext Context; private void AddClassification(JsonToken token, string typeName) { if (!token.IsMissing) - Result!.Add(new ClassifiedSpan(typeName, token.GetSpan())); + Context.AddClassification(typeName, token.GetSpan()); } public void Visit(JsonCompilationUnit node) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedLanguage.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedLanguage.cs index 921a70d8c55b1..31ec97d1c43f7 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedLanguage.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedLanguage.cs @@ -4,13 +4,10 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Classification.Classifiers; using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices; using Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions; -using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.Features.EmbeddedLanguages.RegularExpressions.LanguageServices { @@ -20,7 +17,7 @@ internal class RegexEmbeddedLanguage : IEmbeddedLanguageFeatures private readonly AbstractEmbeddedLanguageFeaturesProvider _provider; - public ISyntaxClassifier Classifier { get; } + public IEmbeddedLanguageClassifier? Classifier { get; } public IDocumentHighlightsService? DocumentHighlightsService { get; } public EmbeddedLanguageCompletionProvider CompletionProvider { get; } @@ -29,7 +26,7 @@ public RegexEmbeddedLanguage( EmbeddedLanguageInfo info) { Info = info; - Classifier = new RegexSyntaxClassifier(info); + Classifier = new RegexEmbeddedLanguageClassifier(info); _provider = provider; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexSyntaxClassifier.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedLanguageClassifier.cs similarity index 86% rename from src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexSyntaxClassifier.cs rename to src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedLanguageClassifier.cs index f76a7cbeaeb64..b8e4cb6b63614 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexSyntaxClassifier.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedLanguageClassifier.cs @@ -2,12 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using System.Collections.Immutable; -using System.Threading; using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Classification.Classifiers; using Microsoft.CodeAnalysis.EmbeddedLanguages.Common; using Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices; using Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions; @@ -23,30 +18,29 @@ namespace Microsoft.CodeAnalysis.Features.EmbeddedLanguages.RegularExpressions.L /// /// Classifier impl for embedded regex strings. /// - internal sealed class RegexSyntaxClassifier : AbstractSyntaxClassifier + internal sealed class RegexEmbeddedLanguageClassifier : IEmbeddedLanguageClassifier { private static readonly ObjectPool s_visitorPool = SharedPools.Default(); private readonly EmbeddedLanguageInfo _info; - public override ImmutableArray SyntaxTokenKinds { get; } - - public RegexSyntaxClassifier(EmbeddedLanguageInfo info) + public RegexEmbeddedLanguageClassifier(EmbeddedLanguageInfo info) { _info = info; - SyntaxTokenKinds = _info.AllStringLiteralKinds; } - public override void AddClassifications( - SyntaxToken token, SemanticModel semanticModel, ClassificationOptions options, - ArrayBuilder result, CancellationToken cancellationToken) + public void RegisterClassifications(EmbeddedLanguageClassifierContext context) { + var token = context.SyntaxToken; if (!_info.IsAnyStringLiteral(token.RawKind)) return; - if (!options.ColorizeRegexPatterns) + if (!context.Options.ColorizeRegexPatterns) return; + var semanticModel = context.SemanticModel; + var cancellationToken = context.CancellationToken; + var detector = RegexLanguageDetector.GetOrCreate(semanticModel.Compilation, _info); var tree = detector.TryParseString(token, semanticModel, cancellationToken); if (tree == null) @@ -55,17 +49,17 @@ public override void AddClassifications( var visitor = s_visitorPool.Allocate(); try { - visitor.Result = result; - AddClassifications(tree.Root, visitor, result); + visitor.Context = context; + AddClassifications(tree.Root, visitor, context); } finally { - visitor.Result = null; + visitor.Context = default; s_visitorPool.Free(visitor); } } - private static void AddClassifications(RegexNode node, Visitor visitor, ArrayBuilder result) + private static void AddClassifications(RegexNode node, Visitor visitor, EmbeddedLanguageClassifierContext context) { node.Accept(visitor); @@ -73,43 +67,38 @@ private static void AddClassifications(RegexNode node, Visitor visitor, ArrayBui { if (child.IsNode) { - AddClassifications(child.Node, visitor, result); + AddClassifications(child.Node, visitor, context); } else { - AddTriviaClassifications(child.Token, result); + AddTriviaClassifications(child.Token, context); } } } - private static void AddTriviaClassifications(RegexToken token, ArrayBuilder result) + private static void AddTriviaClassifications(RegexToken token, EmbeddedLanguageClassifierContext context) { foreach (var trivia in token.LeadingTrivia) - { - AddTriviaClassifications(trivia, result); - } + AddTriviaClassifications(trivia, context); } - private static void AddTriviaClassifications(RegexTrivia trivia, ArrayBuilder result) + private static void AddTriviaClassifications(RegexTrivia trivia, EmbeddedLanguageClassifierContext context) { if (trivia.Kind == RegexKind.CommentTrivia && trivia.VirtualChars.Length > 0) { - result.Add(new ClassifiedSpan( - ClassificationTypeNames.RegexComment, GetSpan(trivia.VirtualChars))); + context.AddClassification(ClassificationTypeNames.RegexComment, GetSpan(trivia.VirtualChars)); } } private class Visitor : IRegexNodeVisitor { - public ArrayBuilder Result; + public EmbeddedLanguageClassifierContext Context; private void AddClassification(RegexToken token, string typeName) { if (!token.IsMissing) - { - Result.Add(new ClassifiedSpan(typeName, token.GetSpan())); - } + Context.AddClassification(typeName, token.GetSpan()); } private void ClassifyWholeNode(RegexNode node, string typeName) @@ -309,10 +298,10 @@ public void Visit(RegexPosixPropertyNode node) { // The .NET parser just interprets the [ of the node, and skips the rest. So // classify the end part as a comment. - Result.Add(new ClassifiedSpan(node.TextToken.VirtualChars[0].Span, ClassificationTypeNames.RegexText)); - Result.Add(new ClassifiedSpan( - GetSpan(node.TextToken.VirtualChars[1], node.TextToken.VirtualChars.Last()), - ClassificationTypeNames.RegexComment)); + Context.AddClassification(ClassificationTypeNames.RegexText, node.TextToken.VirtualChars[0].Span); + Context.AddClassification( + ClassificationTypeNames.RegexComment, + GetSpan(node.TextToken.VirtualChars[1], node.TextToken.VirtualChars.Last())); } public void Visit(RegexAlternationNode node) diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingStackTraceServiceAccessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingStackTraceServiceAccessor.cs index de7badce10c97..10e57513e337c 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingStackTraceServiceAccessor.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingStackTraceServiceAccessor.cs @@ -43,10 +43,9 @@ public async Task> TryParseAsync(s public async Task TryNavigateToAsync(Workspace workspace, UnitTestingDefinitionItemWrapper definitionItem, bool showInPreviewTab, bool activateTab, CancellationToken cancellationToken) { - var location = await definitionItem.UnderlyingObject.GetNavigableLocationAsync( - workspace, new NavigationOptions(showInPreviewTab, activateTab), cancellationToken).ConfigureAwait(false); + var location = await definitionItem.UnderlyingObject.GetNavigableLocationAsync(workspace, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(new NavigationOptions(showInPreviewTab, activateTab), cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDocumentNavigationServiceWrapper.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDocumentNavigationServiceWrapper.cs index b6089eabe8540..4fd780918c3a7 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDocumentNavigationServiceWrapper.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDocumentNavigationServiceWrapper.cs @@ -38,9 +38,9 @@ public bool TryNavigateToPosition(Workspace workspace, DocumentId documentId, in return _threadingProvider.Service.Run(async () => { var location = await obj.GetLocationForPositionAsync( - workspace, documentId, position, virtualSpace, NavigationOptions.Default, cancellationToken).ConfigureAwait(false); + workspace, documentId, position, virtualSpace, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, cancellationToken).ConfigureAwait(false); }); } @@ -50,9 +50,9 @@ public bool TryNavigateToPosition(Workspace workspace, DocumentId documentId, in return _threadingProvider.Service.Run(async () => { var location = await obj.GetLocationForPositionAsync( - workspace, documentId, position, virtualSpace, NavigationOptions.Default, cancellationToken).ConfigureAwait(false); + workspace, documentId, position, virtualSpace, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, cancellationToken).ConfigureAwait(false); }); } } diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItem.DefaultDefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.DefaultDefinitionItem.cs index 97f4e740ac6eb..ebc8751d75aca 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItem.DefaultDefinitionItem.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.DefaultDefinitionItem.cs @@ -51,7 +51,7 @@ public sealed override async Task CanNavigateToAsync(Workspace workspace, return await SourceSpans[0].CanNavigateToAsync(cancellationToken).ConfigureAwait(false); } - public override async Task GetNavigableLocationAsync(Workspace workspace, NavigationOptions options, CancellationToken cancellationToken) + public override async Task GetNavigableLocationAsync(Workspace workspace, CancellationToken cancellationToken) { if (Properties.ContainsKey(NonNavigable)) return null; @@ -65,13 +65,13 @@ public sealed override async Task CanNavigateToAsync(Workspace workspace, var navigationService = workspace.Services.GetRequiredService(); return await navigationService.GetNavigableLocationAsync( - symbol, project, options with { PreferProvisionalTab = true }, cancellationToken).ConfigureAwait(false); + symbol, project, cancellationToken).ConfigureAwait(false); } return null; } - return await SourceSpans[0].GetNavigableLocationAsync(options, cancellationToken).ConfigureAwait(false); + return await SourceSpans[0].GetNavigableLocationAsync(cancellationToken).ConfigureAwait(false); } public DetachedDefinitionItem Detach() diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs index de43ac205efbd..1ecf53f5844a0 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs @@ -166,10 +166,10 @@ protected DefinitionItem( [Obsolete] public virtual bool TryNavigateTo(Workspace workspace, bool showInPreviewTab, bool activateTab, CancellationToken cancellationToken) => false; -#pragma warning disable CS0612 // Type or member is obsolete +#pragma warning disable CS0612 // Type or member is obsolete - TypeScript public virtual Task CanNavigateToAsync(Workspace workspace, CancellationToken cancellationToken) => Task.FromResult(CanNavigateTo(workspace, cancellationToken)); -#pragma warning restore CS0612 // Type or member is obsolete +#pragma warning restore CS0612 // Type or member is obsolete - TypeScript [Obsolete] public virtual Task TryNavigateToAsync(Workspace workspace, bool showInPreviewTab, bool activateTab, CancellationToken cancellationToken) @@ -179,10 +179,10 @@ public virtual Task TryNavigateToAsync(Workspace workspace, bool showInPre public virtual Task TryNavigateToAsync(Workspace workspace, NavigationOptions options, CancellationToken cancellationToken) => TryNavigateToAsync(workspace, options.PreferProvisionalTab, options.ActivateTab, cancellationToken); -#pragma warning disable CS0612 // Type or member is obsolete - public virtual Task GetNavigableLocationAsync(Workspace workspace, NavigationOptions options, CancellationToken cancellationToken) - => Task.FromResult(new NavigableLocation(cancellationToken => TryNavigateToAsync(workspace, options, cancellationToken))); -#pragma warning restore CS0612 // Type or member is obsolete +#pragma warning disable CS0612 // Type or member is obsolete - TypeScript + public virtual Task GetNavigableLocationAsync(Workspace workspace, CancellationToken cancellationToken) + => Task.FromResult(new NavigableLocation((options, cancellationToken) => TryNavigateToAsync(workspace, options, cancellationToken))); +#pragma warning restore CS0612 // Type or member is obsolete - TypeScript public static DefinitionItem Create( ImmutableArray tags, diff --git a/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationService.cs b/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationService.cs index 0c5fb6a838fdb..bef434fed1ef5 100644 --- a/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationService.cs +++ b/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationService.cs @@ -20,13 +20,13 @@ public Task CanNavigateToLineAndOffsetAsync(Workspace workspace, DocumentI public Task CanNavigateToPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken) => SpecializedTasks.False; - public Task GetLocationForSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, NavigationOptions options, bool allowInvalidSpan, CancellationToken cancellationToken) + public Task GetLocationForSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, bool allowInvalidSpan, CancellationToken cancellationToken) => SpecializedTasks.Null(); - public Task GetLocationForLineAndOffsetAsync(Workspace workspace, DocumentId documentId, int lineNumber, int offset, NavigationOptions options, CancellationToken cancellationToken) + public Task GetLocationForLineAndOffsetAsync(Workspace workspace, DocumentId documentId, int lineNumber, int offset, CancellationToken cancellationToken) => SpecializedTasks.Null(); - public Task GetLocationForPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, NavigationOptions options, CancellationToken cancellationToken) + public Task GetLocationForPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken) => SpecializedTasks.Null(); } } diff --git a/src/Features/Core/Portable/Navigation/DefaultSymbolNavigationService.cs b/src/Features/Core/Portable/Navigation/DefaultSymbolNavigationService.cs index 74f22d5125b3d..df32215dfbe9a 100644 --- a/src/Features/Core/Portable/Navigation/DefaultSymbolNavigationService.cs +++ b/src/Features/Core/Portable/Navigation/DefaultSymbolNavigationService.cs @@ -12,16 +12,13 @@ namespace Microsoft.CodeAnalysis.Navigation { internal sealed class DefaultSymbolNavigationService : ISymbolNavigationService { - public Task GetNavigableLocationAsync(ISymbol symbol, Project project, NavigationOptions options, CancellationToken cancellationToken) + public Task GetNavigableLocationAsync(ISymbol symbol, Project project, CancellationToken cancellationToken) => SpecializedTasks.Null(); public Task TrySymbolNavigationNotifyAsync(ISymbol symbol, Project project, CancellationToken cancellationToken) => SpecializedTasks.False; - public Task<(string filePath, LinePosition linePosition)?> GetExternalNavigationSymbolLocationAsync( - DefinitionItem definitionItem, CancellationToken cancellationToken) - { - return Task.FromResult<(string filePath, LinePosition linePosition)?>(null); - } + public Task<(string filePath, LinePosition linePosition)?> GetExternalNavigationSymbolLocationAsync(DefinitionItem definitionItem, CancellationToken cancellationToken) + => Task.FromResult<(string filePath, LinePosition linePosition)?>(null); } } diff --git a/src/Features/Core/Portable/Navigation/IDocumentNavigationService.cs b/src/Features/Core/Portable/Navigation/IDocumentNavigationService.cs index 69a99f76eee90..6a56e34510103 100644 --- a/src/Features/Core/Portable/Navigation/IDocumentNavigationService.cs +++ b/src/Features/Core/Portable/Navigation/IDocumentNavigationService.cs @@ -27,9 +27,9 @@ internal interface IDocumentNavigationService : IWorkspaceService /// Task CanNavigateToPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken); - Task GetLocationForSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, NavigationOptions options, bool allowInvalidSpan, CancellationToken cancellationToken); - Task GetLocationForLineAndOffsetAsync(Workspace workspace, DocumentId documentId, int lineNumber, int offset, NavigationOptions options, CancellationToken cancellationToken); - Task GetLocationForPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, NavigationOptions options, CancellationToken cancellationToken); + Task GetLocationForSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, bool allowInvalidSpan, CancellationToken cancellationToken); + Task GetLocationForLineAndOffsetAsync(Workspace workspace, DocumentId documentId, int lineNumber, int offset, CancellationToken cancellationToken); + Task GetLocationForPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken); } internal static class IDocumentNavigationServiceExtensions @@ -41,12 +41,9 @@ public static Task CanNavigateToPositionAsync(this IDocumentNavigationServ => service.CanNavigateToPositionAsync(workspace, documentId, position, virtualSpace: 0, cancellationToken); public static Task GetLocationForSpanAsync(this IDocumentNavigationService service, Workspace workspace, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken) - => service.GetLocationForSpanAsync(workspace, documentId, textSpan, NavigationOptions.Default, cancellationToken); - - public static Task GetLocationForSpanAsync(this IDocumentNavigationService service, Workspace workspace, DocumentId documentId, TextSpan textSpan, NavigationOptions options, CancellationToken cancellationToken) - => service.GetLocationForSpanAsync(workspace, documentId, textSpan, options, allowInvalidSpan: false, cancellationToken); + => service.GetLocationForSpanAsync(workspace, documentId, textSpan, allowInvalidSpan: false, cancellationToken); public static Task GetLocationForPositionAsync(this IDocumentNavigationService service, Workspace workspace, DocumentId documentId, int position, CancellationToken cancellationToken) - => service.GetLocationForPositionAsync(workspace, documentId, position, virtualSpace: 0, NavigationOptions.Default, cancellationToken); + => service.GetLocationForPositionAsync(workspace, documentId, position, virtualSpace: 0, cancellationToken); } } diff --git a/src/Features/Core/Portable/Navigation/INavigableLocation.cs b/src/Features/Core/Portable/Navigation/INavigableLocation.cs index 0a678c37e44d3..649935a3f736b 100644 --- a/src/Features/Core/Portable/Navigation/INavigableLocation.cs +++ b/src/Features/Core/Portable/Navigation/INavigableLocation.cs @@ -17,18 +17,18 @@ internal interface INavigableLocation /// allow final clients to call this from a non-UI thread while allowing the navigation to jump to the UI /// thread. /// - Task NavigateToAsync(CancellationToken cancellationToken); + Task NavigateToAsync(NavigationOptions options, CancellationToken cancellationToken); } internal class NavigableLocation : INavigableLocation { - private readonly Func> _callback; + private readonly Func> _callback; - public NavigableLocation(Func> callback) + public NavigableLocation(Func> callback) => _callback = callback; - public Task NavigateToAsync(CancellationToken cancellationToken) - => _callback(cancellationToken); + public Task NavigateToAsync(NavigationOptions options, CancellationToken cancellationToken) + => _callback(options, cancellationToken); public static class TestAccessor { @@ -37,7 +37,7 @@ public static class TestAccessor #pragma warning restore VSTHRD200 // Use "Async" suffix for async methods { return Task.FromResult( - new NavigableLocation(c => value ? SpecializedTasks.True : SpecializedTasks.False)); + new NavigableLocation((_, _) => value ? SpecializedTasks.True : SpecializedTasks.False)); } } } diff --git a/src/Features/Core/Portable/Navigation/ISymbolNavigationService.cs b/src/Features/Core/Portable/Navigation/ISymbolNavigationService.cs index 7ad635329cc06..cc1d490a0a5d7 100644 --- a/src/Features/Core/Portable/Navigation/ISymbolNavigationService.cs +++ b/src/Features/Core/Portable/Navigation/ISymbolNavigationService.cs @@ -18,10 +18,8 @@ internal interface ISymbolNavigationService : IWorkspaceService /// A project context with which to generate source for symbol /// if it has no source locations /// The symbol to navigate to - /// A set of options. If these options are not supplied the - /// current set of options from the project's workspace will be used. /// The token to check for cancellation - Task GetNavigableLocationAsync(ISymbol symbol, Project project, NavigationOptions options, CancellationToken cancellationToken); + Task GetNavigableLocationAsync(ISymbol symbol, Project project, CancellationToken cancellationToken); /// True if the navigation was handled, indicating that the caller should not /// perform the navigation. diff --git a/src/Tools/ExternalAccess/FSharp/Navigation/FSharpDocumentNavigationService.cs b/src/Tools/ExternalAccess/FSharp/Navigation/FSharpDocumentNavigationService.cs index 5d73689c253fe..ebf05a79f0785 100644 --- a/src/Tools/ExternalAccess/FSharp/Navigation/FSharpDocumentNavigationService.cs +++ b/src/Tools/ExternalAccess/FSharp/Navigation/FSharpDocumentNavigationService.cs @@ -71,9 +71,9 @@ public bool TryNavigateToSpan(Workspace workspace, DocumentId documentId, TextSp return _threadingContext.JoinableTaskFactory.Run(async () => { var location = await service.GetLocationForSpanAsync( - workspace, documentId, textSpan, NavigationOptions.Default with { PreferProvisionalTab = true }, cancellationToken).ConfigureAwait(false); + workspace, documentId, textSpan, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default with { PreferProvisionalTab = true }, cancellationToken).ConfigureAwait(false); }); } @@ -87,9 +87,9 @@ public bool TryNavigateToLineAndOffset(Workspace workspace, DocumentId documentI return _threadingContext.JoinableTaskFactory.Run(async () => { var location = await service.GetLocationForPositionAsync( - workspace, documentId, lineNumber, offset, NavigationOptions.Default with { PreferProvisionalTab = true }, cancellationToken).ConfigureAwait(false); + workspace, documentId, lineNumber, offset, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default with { PreferProvisionalTab = true }, cancellationToken).ConfigureAwait(false); }); } @@ -103,9 +103,9 @@ public bool TryNavigateToPosition(Workspace workspace, DocumentId documentId, in return _threadingContext.JoinableTaskFactory.Run(async () => { var location = await service.GetLocationForPositionAsync( - workspace, documentId, position, virtualSpace, NavigationOptions.Default with { PreferProvisionalTab = true }, cancellationToken).ConfigureAwait(false); + workspace, documentId, position, virtualSpace, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default with { PreferProvisionalTab = true }, cancellationToken).ConfigureAwait(false); }); } } diff --git a/src/VisualStudio/Core/Def/Implementation/CallHierarchy/CallHierarchyDetail.cs b/src/VisualStudio/Core/Def/Implementation/CallHierarchy/CallHierarchyDetail.cs index ce3aee3880ced..026f935e4b68c 100644 --- a/src/VisualStudio/Core/Def/Implementation/CallHierarchy/CallHierarchyDetail.cs +++ b/src/VisualStudio/Core/Def/Implementation/CallHierarchy/CallHierarchyDetail.cs @@ -74,9 +74,9 @@ public void NavigateTo() _threadingContext.JoinableTaskFactory.Run(async () => { var location = await navigator.GetLocationForSpanAsync( - _workspace, document.Id, _span, options, CancellationToken.None).ConfigureAwait(false); + _workspace, document.Id, _span, CancellationToken.None).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(CancellationToken.None).ConfigureAwait(false); + await location.NavigateToAsync(options, CancellationToken.None).ConfigureAwait(false); }); } } diff --git a/src/VisualStudio/Core/Def/Implementation/CallHierarchy/CallHierarchyProvider.cs b/src/VisualStudio/Core/Def/Implementation/CallHierarchy/CallHierarchyProvider.cs index dcbcf9f17c839..0cab4e58fc300 100644 --- a/src/VisualStudio/Core/Def/Implementation/CallHierarchy/CallHierarchyProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/CallHierarchy/CallHierarchyProvider.cs @@ -148,9 +148,9 @@ public async Task NavigateToAsync(SymbolKey id, Project project, CancellationTok var symbolNavigationService = workspace.Services.GetService(); var location = await symbolNavigationService.GetNavigableLocationAsync( - resolution.Symbol, project, options, cancellationToken).ConfigureAwait(false); + resolution.Symbol, project, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(options, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs index 711efc36a6dad..a5950f537dfa7 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs @@ -310,10 +310,9 @@ public async Task NavigateToAsync(NavigationOptions options, CancellationToken c workspace, _excerptResult.Document.Id, _excerptResult.Span, - options, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(options, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs index 4b892cc1d4470..ee47e92e97f2a 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs @@ -47,11 +47,10 @@ public bool CanNavigateTo() public async Task NavigateToAsync(NavigationOptions options, CancellationToken cancellationToken) { - // Only activate the tab if requested var location = await DefinitionBucket.DefinitionItem.GetNavigableLocationAsync( - Presenter._workspace, options, cancellationToken).ConfigureAwait(false); + Presenter._workspace, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(options, cancellationToken).ConfigureAwait(false); } protected override IList CreateLineTextInlines() diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs index b2fbdd49994d8..8f62d6de0dc76 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs @@ -70,9 +70,9 @@ public bool CanNavigateTo() public async Task NavigateToAsync(NavigationOptions options, CancellationToken cancellationToken) { var location = await DefinitionItem.GetNavigableLocationAsync( - _presenter._workspace, options, cancellationToken).ConfigureAwait(false); + _presenter._workspace, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(options, cancellationToken).ConfigureAwait(false); } public override bool TryGetValue(string key, out object? content) diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMarginContextMenu.xaml.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMarginContextMenu.xaml.cs index 03f24d6ffe1c5..c04a9033ab1a4 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMarginContextMenu.xaml.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMarginContextMenu.xaml.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; @@ -76,7 +77,7 @@ private async Task TargetMenuItem_OnClickAsync(TargetMenuItemViewModel viewModel ImmutableArray.Create(rehydrated), cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(new NavigationOptions(PreferProvisionalTab: true, ActivateTab: true), cancellationToken).ConfigureAwait(false); } private void TargetsSubmenu_OnOpen(object sender, RoutedEventArgs e) diff --git a/src/VisualStudio/Core/Def/Implementation/Progression/GraphNavigatorExtension.cs b/src/VisualStudio/Core/Def/Implementation/Progression/GraphNavigatorExtension.cs index 6aebe3f0ccb3c..3296eb8edf29b 100644 --- a/src/VisualStudio/Core/Def/Implementation/Progression/GraphNavigatorExtension.cs +++ b/src/VisualStudio/Core/Def/Implementation/Progression/GraphNavigatorExtension.cs @@ -107,10 +107,9 @@ await symbolNavigationService.TrySymbolNavigationNotifyAsync(symbol, project, ca document.Id, sourceLocation.StartPosition.Line, sourceLocation.StartPosition.Character, - NavigationOptions.Default, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs index a0ad8457c59f7..81bca0c636f14 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs @@ -167,9 +167,9 @@ protected bool TryNavigateTo(Workspace workspace, DocumentId documentId, LinePos return this.ThreadingContext.JoinableTaskFactory.Run(async () => { var location = await navigationService.GetLocationForLineAndOffsetAsync( - workspace, documentId, position.Line, position.Character, options, cancellationToken).ConfigureAwait(false); + workspace, documentId, position.Line, position.Character, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(options, cancellationToken).ConfigureAwait(false); }); } diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioDocumentNavigationService.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioDocumentNavigationService.cs index 1902d3aa83991..1c8b569f5eda2 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioDocumentNavigationService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioDocumentNavigationService.cs @@ -126,7 +126,7 @@ public async Task CanNavigateToPositionAsync(Workspace workspace, Document } public async Task GetLocationForSpanAsync( - Workspace workspace, DocumentId documentId, TextSpan textSpan, NavigationOptions options, bool allowInvalidSpan, CancellationToken cancellationToken) + Workspace workspace, DocumentId documentId, TextSpan textSpan, bool allowInvalidSpan, CancellationToken cancellationToken) { if (!await this.CanNavigateToSpanAsync(workspace, documentId, textSpan, allowInvalidSpan, cancellationToken).ConfigureAwait(false)) return null; @@ -135,12 +135,11 @@ public async Task CanNavigateToPositionAsync(Workspace workspace, Document documentId, _ => Task.FromResult(textSpan), text => GetVsTextSpan(text, textSpan, allowInvalidSpan), - options, cancellationToken).ConfigureAwait(false); } public async Task GetLocationForLineAndOffsetAsync( - Workspace workspace, DocumentId documentId, int lineNumber, int offset, NavigationOptions options, CancellationToken cancellationToken) + Workspace workspace, DocumentId documentId, int lineNumber, int offset, CancellationToken cancellationToken) { if (!await this.CanNavigateToLineAndOffsetAsync(workspace, documentId, lineNumber, offset, cancellationToken).ConfigureAwait(false)) return null; @@ -149,7 +148,6 @@ public async Task CanNavigateToPositionAsync(Workspace workspace, Document documentId, document => GetTextSpanFromLineAndOffsetAsync(document, lineNumber, offset, cancellationToken), text => GetVsTextSpan(text, lineNumber, offset), - options, cancellationToken).ConfigureAwait(false); static async Task GetTextSpanFromLineAndOffsetAsync(Document document, int lineNumber, int offset, CancellationToken cancellationToken) @@ -167,7 +165,7 @@ static VsTextSpan GetVsTextSpan(SourceText text, int lineNumber, int offset) } public async Task GetLocationForPositionAsync( - Workspace workspace, DocumentId documentId, int position, int virtualSpace, NavigationOptions options, CancellationToken cancellationToken) + Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken) { if (!await this.CanNavigateToPositionAsync(workspace, documentId, position, virtualSpace, cancellationToken).ConfigureAwait(false)) return null; @@ -176,7 +174,6 @@ static VsTextSpan GetVsTextSpan(SourceText text, int lineNumber, int offset) documentId, document => GetTextSpanFromPositionAsync(document, position, virtualSpace, cancellationToken), text => GetVsTextSpan(text, position, virtualSpace), - options, cancellationToken).ConfigureAwait(false); static async Task GetTextSpanFromPositionAsync(Document document, int position, int virtualSpace, CancellationToken cancellationToken) @@ -213,7 +210,6 @@ static VsTextSpan GetVsTextSpan(SourceText text, int position, int virtualSpace) DocumentId documentId, Func> getTextSpanForMappingAsync, Func getVsTextSpan, - NavigationOptions options, CancellationToken cancellationToken) { var callback = await GetNavigationCallbackAsync( @@ -221,7 +217,7 @@ static VsTextSpan GetVsTextSpan(SourceText text, int position, int virtualSpace) if (callback == null) return null; - return new NavigableLocation(async cancellationToken => + return new NavigableLocation(async (options, cancellationToken) => { await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); using (OpenNewDocumentStateScope(options)) diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioSymbolNavigationService.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioSymbolNavigationService.cs index e18d784d19b11..4c168d6f2352c 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioSymbolNavigationService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioSymbolNavigationService.cs @@ -56,7 +56,7 @@ public VisualStudioSymbolNavigationService( } public async Task GetNavigableLocationAsync( - ISymbol symbol, Project project, NavigationOptions options, CancellationToken cancellationToken) + ISymbol symbol, Project project, CancellationToken cancellationToken) { if (project == null || symbol == null) { @@ -79,7 +79,7 @@ public VisualStudioSymbolNavigationService( var navigationService = editorWorkspace.Services.GetRequiredService(); return await navigationService.GetLocationForSpanAsync( editorWorkspace, targetDocument.Id, sourceLocation.SourceSpan, - options, allowInvalidSpan: false, cancellationToken).ConfigureAwait(false); + allowInvalidSpan: false, cancellationToken).ConfigureAwait(false); } } @@ -109,7 +109,7 @@ public VisualStudioSymbolNavigationService( if (navInfo != null) { var navigationTool = IServiceProviderExtensions.GetService(_serviceProvider); - return new NavigableLocation(async cancellationToken => + return new NavigableLocation(async (options, cancellationToken) => { await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); return navigationTool.NavigateToNavInfo(navInfo) == VSConstants.S_OK; @@ -120,17 +120,17 @@ public VisualStudioSymbolNavigationService( } // Generate new source or retrieve existing source for the symbol in question - return await GetNavigableLocationForMetadataAsync(project, symbol, options, cancellationToken).ConfigureAwait(false); + return await GetNavigableLocationForMetadataAsync(project, symbol, cancellationToken).ConfigureAwait(false); } private async Task GetNavigableLocationForMetadataAsync( - Project project, ISymbol symbol, NavigationOptions options, CancellationToken cancellationToken) + Project project, ISymbol symbol, CancellationToken cancellationToken) { var allowDecompilation = _globalOptions.GetOption(FeatureOnOffOptions.NavigateToDecompiledSources); var result = await _metadataAsSourceFileService.GetGeneratedFileAsync(project, symbol, signaturesOnly: false, allowDecompilation, cancellationToken).ConfigureAwait(false); - return new NavigableLocation(async cancellationToken => + return new NavigableLocation(async (options, cancellationToken) => { await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -170,10 +170,9 @@ public VisualStudioSymbolNavigationService( editorWorkspace, openedDocument.Id, result.IdentifierLocation.SourceSpan, - options with { PreferProvisionalTab = true }, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(options with { PreferProvisionalTab = true }, cancellationToken).ConfigureAwait(false); } return true; diff --git a/src/VisualStudio/Core/Def/StackTraceExplorer/StackFrameViewModel.cs b/src/VisualStudio/Core/Def/StackTraceExplorer/StackFrameViewModel.cs index 83cfa1112bf6f..bf75e9ba1fee2 100644 --- a/src/VisualStudio/Core/Def/StackTraceExplorer/StackFrameViewModel.cs +++ b/src/VisualStudio/Core/Def/StackTraceExplorer/StackFrameViewModel.cs @@ -84,9 +84,9 @@ private async Task NavigateToDefinitionAsync(DefinitionItem? definition, Cancell if (canNavigate) { var location = await definition.GetNavigableLocationAsync( - _workspace, new NavigationOptions(PreferProvisionalTab: true, ActivateTab: false), cancellationToken).ConfigureAwait(false); + _workspace, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(new NavigationOptions(PreferProvisionalTab: true, ActivateTab: false), cancellationToken).ConfigureAwait(false); } } @@ -141,9 +141,9 @@ public async Task NavigateToFileAsync(CancellationToken cancellationToken) await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var location = await navigationService.GetLocationForLineAndOffsetAsync( - _workspace, document.Id, lineNumber - 1, offset: 0, options, cancellationToken).ConfigureAwait(false); + _workspace, document.Id, lineNumber - 1, offset: 0, cancellationToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(options, cancellationToken).ConfigureAwait(false); } } catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex, cancellationToken)) diff --git a/src/VisualStudio/Core/Def/ValueTracking/TreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/TreeItemViewModel.cs index 055159e62a0f1..6171349e52d1b 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/TreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/TreeItemViewModel.cs @@ -116,9 +116,9 @@ public virtual void NavigateTo() this.ThreadingContext.JoinableTaskFactory.Run(async () => { var location = await navigationService.GetLocationForLineAndOffsetAsync( - Workspace, DocumentId, LineSpan.Start, 0, options, ThreadingContext.DisposalToken).ConfigureAwait(false); + Workspace, DocumentId, LineSpan.Start, 0, ThreadingContext.DisposalToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(ThreadingContext.DisposalToken).ConfigureAwait(false); + await location.NavigateToAsync(options, ThreadingContext.DisposalToken).ConfigureAwait(false); }); } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index 2f22e7f0b780c..d3f03b9249946 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -166,9 +166,9 @@ public override void NavigateTo() this.ThreadingContext.JoinableTaskFactory.Run(async () => { var location = await navigationService.GetLocationForSpanAsync( - Workspace, DocumentId, _trackedItem.Span, options, ThreadingContext.DisposalToken).ConfigureAwait(false); + Workspace, DocumentId, _trackedItem.Span, ThreadingContext.DisposalToken).ConfigureAwait(false); if (location != null) - await location.NavigateToAsync(ThreadingContext.DisposalToken).ConfigureAwait(false); + await location.NavigateToAsync(options, ThreadingContext.DisposalToken).ConfigureAwait(false); }); } diff --git a/src/VisualStudio/Core/Impl/RoslynVisualStudioWorkspace.cs b/src/VisualStudio/Core/Impl/RoslynVisualStudioWorkspace.cs index 9377bbfe96d7a..0f3566d0b612e 100644 --- a/src/VisualStudio/Core/Impl/RoslynVisualStudioWorkspace.cs +++ b/src/VisualStudio/Core/Impl/RoslynVisualStudioWorkspace.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Editor.Undo; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; @@ -109,7 +110,7 @@ public override async Task TryGoToDefinitionAsync( symbolInfo.Symbol, currentProject.Solution, _threadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false); return location != null && - await location.NavigateToAsync(cancellationToken).ConfigureAwait(false); + await location.NavigateToAsync(new NavigationOptions(PreferProvisionalTab: true, ActivateTab: true), cancellationToken).ConfigureAwait(false); } public override bool TryFindAllReferences(ISymbol symbol, Project project, CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItem.cs index a1d4ce3218991..c555bea0c8eb4 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItem.cs @@ -78,7 +78,7 @@ public bool Invoke(IEnumerable items, InputSource inputSource, bool prev var location = await documentNavigationService.GetLocationForPositionAsync( item.Workspace, item.DocumentId, position: 0, CancellationToken.None).ConfigureAwait(false); didNavigate |= location != null && - await location.NavigateToAsync(CancellationToken.None).ConfigureAwait(false); + await location.NavigateToAsync(NavigationOptions.Default, CancellationToken.None).ConfigureAwait(false); } } diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 240b7ac2dea93..bcd7d41bf5e83 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -523,10 +523,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData) End Function - Public Function TryAppendDiagnosticsForSpanAsync(document As Document, range As TextSpan, diagnostics As ArrayBuilder(Of DiagnosticData), Optional includeSuppressedDiagnostics As Boolean = False, Optional cancellationToken As CancellationToken = Nothing) As Task(Of Boolean) Implements IDiagnosticAnalyzerService.TryAppendDiagnosticsForSpanAsync - Return Task.FromResult(False) - End Function - Public Function GetSpecificCachedDiagnosticsAsync(workspace As Workspace, id As Object, Optional includeSuppressedDiagnostics As Boolean = False, Optional cancellationToken As CancellationToken = Nothing) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetSpecificCachedDiagnosticsAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function @@ -554,6 +550,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Function ForceAnalyzeAsync(solution As Solution, onProjectAnalyzed As Action(Of Project), Optional projectId As ProjectId = Nothing, Optional cancellationToken As CancellationToken = Nothing) As Task Implements IDiagnosticAnalyzerService.ForceAnalyzeAsync Throw New NotImplementedException() End Function + + Public Function TryGetDiagnosticsForSpanAsync(document As Document, range As TextSpan, shouldIncludeDiagnostic As Func(Of String, Boolean), Optional includeSuppressedDiagnostics As Boolean = False, Optional priority As CodeActionRequestPriority = CodeActionRequestPriority.None, Optional cancellationToken As CancellationToken = Nothing) As Task(Of (diagnostics As ImmutableArray(Of DiagnosticData), upToDate As Boolean)) Implements IDiagnosticAnalyzerService.TryGetDiagnosticsForSpanAsync + Return Task.FromResult((ImmutableArray(Of DiagnosticData).Empty, False)) + End Function End Class End Class End Namespace diff --git a/src/VisualStudio/Core/Test/GoToDefinition/GoToDefinitionApiTests.vb b/src/VisualStudio/Core/Test/GoToDefinition/GoToDefinitionApiTests.vb index 0060889213f58..22a1102bb8d38 100644 --- a/src/VisualStudio/Core/Test/GoToDefinition/GoToDefinitionApiTests.vb +++ b/src/VisualStudio/Core/Test/GoToDefinition/GoToDefinitionApiTests.vb @@ -8,6 +8,7 @@ Imports Microsoft.CodeAnalysis.Editor.GoToDefinition Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.Navigation Imports Microsoft.CodeAnalysis.Test.Utilities Imports Roslyn.Test.Utilities Imports Roslyn.Utilities @@ -50,7 +51,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.GoToDefinition thirdPartyNavigationAllowed:=True, cancellationToken:=CancellationToken.None) Dim success = location IsNot Nothing AndAlso - Await location.NavigateToAsync(CancellationToken.None) + Await location.NavigateToAsync(NavigationOptions.Default, CancellationToken.None) Assert.Equal(expectSuccess, success) End Using diff --git a/src/VisualStudio/LiveShare/Test/MockDocumentNavigationServiceFactory.cs b/src/VisualStudio/LiveShare/Test/MockDocumentNavigationServiceFactory.cs index ed86f2adc6a1e..99a8f8107fb06 100644 --- a/src/VisualStudio/LiveShare/Test/MockDocumentNavigationServiceFactory.cs +++ b/src/VisualStudio/LiveShare/Test/MockDocumentNavigationServiceFactory.cs @@ -39,13 +39,13 @@ private class MockDocumentNavigationService : IDocumentNavigationService public Task CanNavigateToSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, bool allowInvalidSpan, CancellationToken cancellationToken) => SpecializedTasks.True; - public Task GetLocationForLineAndOffsetAsync(Workspace workspace, DocumentId documentId, int lineNumber, int offset, NavigationOptions options, CancellationToken cancellationToken) + public Task GetLocationForLineAndOffsetAsync(Workspace workspace, DocumentId documentId, int lineNumber, int offset, CancellationToken cancellationToken) => NavigableLocation.TestAccessor.Create(true); - public Task GetLocationForPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, NavigationOptions options, CancellationToken cancellationToken) + public Task GetLocationForPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken) => NavigableLocation.TestAccessor.Create(true); - public Task GetLocationForSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, NavigationOptions options, bool allowInvalidSpan, CancellationToken cancellationToken) + public Task GetLocationForSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, bool allowInvalidSpan, CancellationToken cancellationToken) => NavigableLocation.TestAccessor.Create(true); } } diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/CSharpEmbeddedLanguageClassificationServiceFactory.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/CSharpEmbeddedLanguageClassificationServiceFactory.cs index 37100654e9e3d..5565b4b703891 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/CSharpEmbeddedLanguageClassificationServiceFactory.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/CSharpEmbeddedLanguageClassificationServiceFactory.cs @@ -6,6 +6,7 @@ using System.Composition; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.CSharp.LanguageServices; using Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; @@ -22,6 +23,6 @@ public CSharpEmbeddedLanguageClassificationServiceFactory() } public ILanguageService CreateLanguageService(HostLanguageServices languageServices) - => new EmbeddedLanguageClassificationService(languageServices.GetRequiredService()); + => new EmbeddedLanguageClassificationService(languageServices.GetRequiredService(), CSharpSyntaxKinds.Instance); } } diff --git a/src/Workspaces/Core/Portable/Classification/ClassifiedSpan.cs b/src/Workspaces/Core/Portable/Classification/ClassifiedSpan.cs index 7b81261818ea2..d15f0ed85d1e8 100644 --- a/src/Workspaces/Core/Portable/Classification/ClassifiedSpan.cs +++ b/src/Workspaces/Core/Portable/Classification/ClassifiedSpan.cs @@ -19,7 +19,6 @@ public ClassifiedSpan(string classificationType, TextSpan textSpan) } public ClassifiedSpan(TextSpan textSpan, string classificationType) - : this() { this.ClassificationType = classificationType; this.TextSpan = textSpan; diff --git a/src/Workspaces/Core/Portable/Classification/Classifier.cs b/src/Workspaces/Core/Portable/Classification/Classifier.cs index 0c4731f6fde8f..dc72199e92c87 100644 --- a/src/Workspaces/Core/Portable/Classification/Classifier.cs +++ b/src/Workspaces/Core/Portable/Classification/Classifier.cs @@ -59,6 +59,7 @@ internal static IEnumerable GetClassifiedSpans( { var languageServices = workspaceServices.GetLanguageServices(semanticModel.Language); var classsificationService = languageServices.GetRequiredService(); + var embeddedLanguageService = languageServices.GetRequiredService(); var syntaxClassifiers = classsificationService.GetDefaultSyntaxClassifiers(); @@ -74,12 +75,8 @@ internal static IEnumerable GetClassifiedSpans( classsificationService.AddSyntacticClassifications(root, textSpan, syntacticClassifications, cancellationToken); classsificationService.AddSemanticClassifications(semanticModel, textSpan, getNodeClassifiers, getTokenClassifiers, semanticClassifications, options, cancellationToken); - var embeddedLanguageService = languageServices.GetService(); - if (embeddedLanguageService != null) - { - // intentionally adding to the semanticClassifications array here. - embeddedLanguageService.AddEmbeddedLanguageClassifications(semanticModel, textSpan, options, semanticClassifications, cancellationToken); - } + // intentionally adding to the semanticClassifications array here. + embeddedLanguageService.AddEmbeddedLanguageClassifications(semanticModel, textSpan, options, semanticClassifications, cancellationToken); var allClassifications = new List(semanticClassifications.Where(s => s.TextSpan.OverlapsWith(textSpan))); var semanticSet = semanticClassifications.Select(s => s.TextSpan).ToSet(); diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/EmbeddedLanguageClassificationService.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/EmbeddedLanguageClassificationService.cs index a6798f4be85a3..fbfbc88b59760 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/EmbeddedLanguageClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/EmbeddedLanguageClassificationService.cs @@ -3,14 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Classification.Classifiers { @@ -20,12 +19,21 @@ internal sealed class EmbeddedLanguageClassificationService : IEmbeddedLanguageC private readonly HashSet _syntaxTokenKinds = new(); - public EmbeddedLanguageClassificationService(IEmbeddedLanguagesProvider languagesProvider) + public EmbeddedLanguageClassificationService( + IEmbeddedLanguagesProvider languagesProvider, + ISyntaxKinds syntaxKinds) { _languagesProvider = languagesProvider; - _syntaxTokenKinds.AddRange( - languagesProvider.Languages.Where(p => p.Classifier != null) - .SelectMany(p => p.Classifier.SyntaxTokenKinds)); + + _syntaxTokenKinds.Add(syntaxKinds.CharacterLiteralToken); + _syntaxTokenKinds.Add(syntaxKinds.StringLiteralToken); + _syntaxTokenKinds.Add(syntaxKinds.InterpolatedStringTextToken); + + if (syntaxKinds.SingleLineRawStringLiteralToken != null) + _syntaxTokenKinds.Add(syntaxKinds.SingleLineRawStringLiteralToken.Value); + + if (syntaxKinds.MultiLineRawStringLiteralToken != null) + _syntaxTokenKinds.Add(syntaxKinds.MultiLineRawStringLiteralToken.Value); } public async Task AddEmbeddedLanguageClassificationsAsync( @@ -39,8 +47,8 @@ public void AddEmbeddedLanguageClassifications( SemanticModel semanticModel, TextSpan textSpan, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken) { var root = semanticModel.SyntaxTree.GetRoot(cancellationToken); - var worker = new Worker(_languagesProvider, _syntaxTokenKinds, semanticModel, textSpan, options, result); - worker.Recurse(root, cancellationToken); + var worker = new Worker(_languagesProvider, _syntaxTokenKinds, semanticModel, textSpan, options, result, cancellationToken); + worker.Recurse(root); } private ref struct Worker @@ -51,63 +59,66 @@ private ref struct Worker private readonly TextSpan _textSpan; private readonly ClassificationOptions _options; private readonly ArrayBuilder _result; + private readonly CancellationToken _cancellationToken; public Worker( - IEmbeddedLanguagesProvider _languagesProvider, - HashSet _syntaxTokenKinds, + IEmbeddedLanguagesProvider languagesProvider, + HashSet syntaxTokenKinds, SemanticModel semanticModel, TextSpan textSpan, ClassificationOptions options, - ArrayBuilder result) + ArrayBuilder result, + CancellationToken cancellationToken) { - this._languagesProvider = _languagesProvider; - this._syntaxTokenKinds = _syntaxTokenKinds; + _languagesProvider = languagesProvider; + _syntaxTokenKinds = syntaxTokenKinds; _semanticModel = semanticModel; _textSpan = textSpan; _options = options; _result = result; + _cancellationToken = cancellationToken; } - public void Recurse( - SyntaxNode node, - CancellationToken cancellationToken) + public void Recurse(SyntaxNode node) { - cancellationToken.ThrowIfCancellationRequested(); + _cancellationToken.ThrowIfCancellationRequested(); if (node.Span.IntersectsWith(_textSpan)) { foreach (var child in node.ChildNodesAndTokens()) { if (child.IsNode) { - Recurse(child.AsNode()!, cancellationToken); + Recurse(child.AsNode()!); } else { - ProcessToken(child.AsToken(), cancellationToken); + ProcessToken(child.AsToken()); } } } } - private void ProcessToken(SyntaxToken token, CancellationToken cancellationToken) + private void ProcessToken(SyntaxToken token) { - cancellationToken.ThrowIfCancellationRequested(); - ProcessTriviaList(token.LeadingTrivia, cancellationToken); - ClassifyToken(token, cancellationToken); - ProcessTriviaList(token.TrailingTrivia, cancellationToken); + _cancellationToken.ThrowIfCancellationRequested(); + ProcessTriviaList(token.LeadingTrivia); + ClassifyToken(token); + ProcessTriviaList(token.TrailingTrivia); } - private readonly void ClassifyToken(SyntaxToken token, CancellationToken cancellationToken) + private readonly void ClassifyToken(SyntaxToken token) { if (token.Span.IntersectsWith(_textSpan) && _syntaxTokenKinds.Contains(token.RawKind)) { + var context = new EmbeddedLanguageClassifierContext( + _semanticModel, token, _options, _result, _cancellationToken); foreach (var language in _languagesProvider.Languages) { var classifier = language.Classifier; if (classifier != null) { var count = _result.Count; - classifier.AddClassifications(token, _semanticModel, _options, _result, cancellationToken); + classifier.RegisterClassifications(context); if (_result.Count != count) { // This classifier added values. No need to check the other ones. @@ -118,16 +129,16 @@ private readonly void ClassifyToken(SyntaxToken token, CancellationToken cancell } } - private void ProcessTriviaList(SyntaxTriviaList triviaList, CancellationToken cancellationToken) + private void ProcessTriviaList(SyntaxTriviaList triviaList) { foreach (var trivia in triviaList) - ProcessTrivia(trivia, cancellationToken); + ProcessTrivia(trivia); } - private void ProcessTrivia(SyntaxTrivia trivia, CancellationToken cancellationToken) + private void ProcessTrivia(SyntaxTrivia trivia) { if (trivia.HasStructure && trivia.FullSpan.IntersectsWith(_textSpan)) - Recurse(trivia.GetStructure()!, cancellationToken); + Recurse(trivia.GetStructure()!); } } } diff --git a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/EmbeddedLanguageClassifierContext.cs b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/EmbeddedLanguageClassifierContext.cs new file mode 100644 index 0000000000000..1510db840f523 --- /dev/null +++ b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/EmbeddedLanguageClassifierContext.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices +{ + internal struct EmbeddedLanguageClassifierContext + { + internal readonly ClassificationOptions Options; + private readonly ArrayBuilder _result; + + /// + /// The string or character token to classify. + /// + public SyntaxToken SyntaxToken { get; } + + /// + /// SemanticModel that is contained in. + /// + public SemanticModel SemanticModel { get; } + + public CancellationToken CancellationToken { get; } + + internal EmbeddedLanguageClassifierContext( + SemanticModel semanticModel, + SyntaxToken syntaxToken, + ClassificationOptions options, + ArrayBuilder result, + CancellationToken cancellationToken) + { + SemanticModel = semanticModel; + SyntaxToken = syntaxToken; + Options = options; + _result = result; + CancellationToken = cancellationToken; + } + + public void AddClassification(string classificationType, TextSpan span) + => _result.Add(new ClassifiedSpan(classificationType, span)); + } +} diff --git a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackEmbeddedLanguage.cs b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackEmbeddedLanguage.cs index 8491c62997135..7fc979365a11d 100644 --- a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackEmbeddedLanguage.cs +++ b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackEmbeddedLanguage.cs @@ -12,9 +12,9 @@ namespace Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices /// internal partial class FallbackEmbeddedLanguage : IEmbeddedLanguage { - public ISyntaxClassifier Classifier { get; } + public IEmbeddedLanguageClassifier Classifier { get; } public FallbackEmbeddedLanguage(EmbeddedLanguageInfo info) - => Classifier = new FallbackSyntaxClassifier(info); + => Classifier = new FallbackEmbeddedLanguageClassifier(info); } } diff --git a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackSyntaxClassifier.cs b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackEmbeddedLanguageClassifier.cs similarity index 61% rename from src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackSyntaxClassifier.cs rename to src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackEmbeddedLanguageClassifier.cs index 76bb4f5cd772a..8449cb86b64b8 100644 --- a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackSyntaxClassifier.cs +++ b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackEmbeddedLanguageClassifier.cs @@ -3,33 +3,28 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Threading; using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Classification.Classifiers; -using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices { - internal class FallbackSyntaxClassifier : AbstractSyntaxClassifier + internal class FallbackEmbeddedLanguageClassifier : IEmbeddedLanguageClassifier { private readonly EmbeddedLanguageInfo _info; + private readonly ImmutableArray _supportedKinds; - public override ImmutableArray SyntaxTokenKinds { get; } - - public FallbackSyntaxClassifier(EmbeddedLanguageInfo info) + public FallbackEmbeddedLanguageClassifier(EmbeddedLanguageInfo info) { _info = info; - SyntaxTokenKinds = ImmutableArray.Create( + _supportedKinds = ImmutableArray.Create( info.SyntaxKinds.CharacterLiteralToken, info.SyntaxKinds.StringLiteralToken, info.SyntaxKinds.InterpolatedStringTextToken); } - public override void AddClassifications( - SyntaxToken token, SemanticModel semanticModel, ClassificationOptions options, - ArrayBuilder result, CancellationToken cancellationToken) + public void RegisterClassifications(EmbeddedLanguageClassifierContext context) { - if (!SyntaxTokenKinds.Contains(token.RawKind)) + var token = context.SyntaxToken; + if (!_supportedKinds.Contains(token.RawKind)) return; var virtualChars = _info.VirtualCharService.TryConvertToVirtualChars(token); @@ -44,7 +39,7 @@ public override void AddClassifications( foreach (var vc in virtualChars) { if (vc.Span.Length > 1) - result.Add(new ClassifiedSpan(ClassificationTypeNames.StringEscapeCharacter, vc.Span)); + context.AddClassification(ClassificationTypeNames.StringEscapeCharacter, vc.Span); } } } diff --git a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/IEmbeddedLanguage.cs b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/IEmbeddedLanguage.cs index 74f86058c4d90..0ff88013ba1d8 100644 --- a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/IEmbeddedLanguage.cs +++ b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/IEmbeddedLanguage.cs @@ -2,10 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Classification.Classifiers; namespace Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices { @@ -17,6 +14,6 @@ internal interface IEmbeddedLanguage /// /// A optional classifier that can produce s for an embedded language string. /// - ISyntaxClassifier Classifier { get; } + IEmbeddedLanguageClassifier? Classifier { get; } } } diff --git a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/IEmbeddedLanguageClassifier.cs b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/IEmbeddedLanguageClassifier.cs new file mode 100644 index 0000000000000..054bf6e8265fe --- /dev/null +++ b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/IEmbeddedLanguageClassifier.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices +{ + internal interface IEmbeddedLanguageClassifier + { + /// + /// This method will be called for all string and character tokens in a file to determine if there are special + /// embedded language strings to classify. + /// + void RegisterClassifications(EmbeddedLanguageClassifierContext context); + } +} diff --git a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/VisualBasicEmbeddedLanguageClassificationServiceFactory.vb b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/VisualBasicEmbeddedLanguageClassificationServiceFactory.vb index 83f8e1caace0d..fcd2626f57183 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/VisualBasicEmbeddedLanguageClassificationServiceFactory.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/VisualBasicEmbeddedLanguageClassificationServiceFactory.vb @@ -8,6 +8,7 @@ Imports Microsoft.CodeAnalysis.Classification.Classifiers Imports Microsoft.CodeAnalysis.EmbeddedLanguages.LanguageServices Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices Namespace Microsoft.CodeAnalysis.VisualBasic.Classification @@ -20,7 +21,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification End Sub Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService - Return New EmbeddedLanguageClassificationService(languageServices.GetRequiredService(Of IEmbeddedLanguagesProvider)) + Return New EmbeddedLanguageClassificationService(languageServices.GetRequiredService(Of IEmbeddedLanguagesProvider), VisualBasicSyntaxKinds.Instance) End Function End Class End Namespace