From 741ea40c0ca3cc363ce2859362612d5e518915a1 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Fri, 25 Feb 2022 09:42:12 -0800 Subject: [PATCH 1/6] Pass Roslyn servies options explicitly --- build/Packages.props | 2 +- ...SharpFormattingWorkspaceOptionsProvider.cs | 32 +--- .../Services/Completion/CompletionService.cs | 4 +- .../DecompilationExternalSourceService.cs | 5 +- .../OmniSharpCSharpDecompiledSourceService.cs | 13 +- .../Services/Formatting/CodeFormatService.cs | 8 +- .../Formatting/FormatAfterKeystrokeService.cs | 6 +- .../Services/Formatting/FormatRangeService.cs | 6 +- .../ImplementTypeWorkspaceOptionsProvider.cs | 35 ----- .../Intellisense/IntellisenseService.cs | 2 +- .../Services/Refactoring/FixUsingService.cs | 9 +- .../Refactoring/GetFixAllCodeActionService.cs | 6 +- .../Refactoring/RunFixAllCodeActionService.cs | 20 ++- .../Refactoring/V2/BaseCodeActionService.cs | 18 ++- .../Refactoring/V2/GetCodeActionsService.cs | 6 +- .../Refactoring/V2/RunCodeActionService.cs | 6 +- .../CSharpDiagnosticWorkerWithAnalyzers.cs | 25 ++-- .../Workers/Formatting/FormattingWorker.cs | 140 +++++++++++++++--- .../Workers/Refactoring/FixUsingsWorker.cs | 10 +- .../Utilities/CodeActionOptionsFactory.cs | 14 ++ .../Options/FormattingOptions.cs | 5 +- .../Options/ImplementTypeOptions.cs | 4 +- .../FormatAfterKeystrokeTests.cs | 3 +- .../FormattingFacts.cs | 21 +-- tests/TestUtility/AbstractTestFixture.cs | 4 +- tests/TestUtility/ConfigurationHelpers.cs | 7 +- 26 files changed, 246 insertions(+), 165 deletions(-) delete mode 100644 src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs create mode 100644 src/OmniSharp.Roslyn/Utilities/CodeActionOptionsFactory.cs diff --git a/build/Packages.props b/build/Packages.props index e20ddb18aa..1329e39342 100644 --- a/build/Packages.props +++ b/build/Packages.props @@ -7,7 +7,7 @@ 17.0.0 17.0.0 6.0.0 - 4.2.0-1.22074.8 + 4.2.0-2.22124.7 2.4.1 diff --git a/src/OmniSharp.Roslyn.CSharp/Services/CSharpFormattingWorkspaceOptionsProvider.cs b/src/OmniSharp.Roslyn.CSharp/Services/CSharpFormattingWorkspaceOptionsProvider.cs index 3ca55e825f..385a5e6eb5 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/CSharpFormattingWorkspaceOptionsProvider.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/CSharpFormattingWorkspaceOptionsProvider.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.Options; using OmniSharp.Options; +using OmniSharp.Roslyn.CSharp.Workers.Formatting; using OmniSharp.Roslyn.Options; using RoslynFormattingOptions = Microsoft.CodeAnalysis.Formatting.FormattingOptions; @@ -32,7 +33,6 @@ private static OptionSet GetOptions(OptionSet optionSet, FormattingOptions forma .WithChangedOption(CSharpFormattingOptions.SpaceWithinCastParentheses, formattingOptions.SpaceWithinCastParentheses) .WithChangedOption(CSharpFormattingOptions.SpaceWithinOtherParentheses, formattingOptions.SpaceWithinOtherParentheses) .WithChangedOption(CSharpFormattingOptions.SpaceAfterCast, formattingOptions.SpaceAfterCast) - .WithChangedOption(CSharpFormattingOptions.SpacesIgnoreAroundVariableDeclaration, formattingOptions.SpacesIgnoreAroundVariableDeclaration) .WithChangedOption(CSharpFormattingOptions.SpaceBeforeOpenSquareBracket, formattingOptions.SpaceBeforeOpenSquareBracket) .WithChangedOption(CSharpFormattingOptions.SpaceBetweenEmptySquareBrackets, formattingOptions.SpaceBetweenEmptySquareBrackets) .WithChangedOption(CSharpFormattingOptions.SpaceWithinSquareBrackets, formattingOptions.SpaceWithinSquareBrackets) @@ -44,13 +44,13 @@ private static OptionSet GetOptions(OptionSet optionSet, FormattingOptions forma .WithChangedOption(CSharpFormattingOptions.SpaceBeforeComma, formattingOptions.SpaceBeforeComma) .WithChangedOption(CSharpFormattingOptions.SpaceBeforeDot, formattingOptions.SpaceBeforeDot) .WithChangedOption(CSharpFormattingOptions.SpaceBeforeSemicolonsInForStatement, formattingOptions.SpaceBeforeSemicolonsInForStatement) - .WithChangedOption(CSharpFormattingOptions.SpacingAroundBinaryOperator, BinaryOperatorSpacingOptionForStringValue(formattingOptions.SpacingAroundBinaryOperator)) + .WithChangedOption(CSharpFormattingOptions.SpacingAroundBinaryOperator, FormattingWorker.BinaryOperatorSpacingOptionForStringValue(formattingOptions.SpacingAroundBinaryOperator)) .WithChangedOption(CSharpFormattingOptions.IndentBraces, formattingOptions.IndentBraces) .WithChangedOption(CSharpFormattingOptions.IndentBlock, formattingOptions.IndentBlock) .WithChangedOption(CSharpFormattingOptions.IndentSwitchSection, formattingOptions.IndentSwitchSection) .WithChangedOption(CSharpFormattingOptions.IndentSwitchCaseSection, formattingOptions.IndentSwitchCaseSection) .WithChangedOption(CSharpFormattingOptions.IndentSwitchCaseSectionWhenBlock, formattingOptions.IndentSwitchCaseSectionWhenBlock) - .WithChangedOption(CSharpFormattingOptions.LabelPositioning, LabelPositionOptionForStringValue(formattingOptions.LabelPositioning)) + .WithChangedOption(CSharpFormattingOptions.LabelPositioning, FormattingWorker.LabelPositionOptionForStringValue(formattingOptions.LabelPositioning)) .WithChangedOption(CSharpFormattingOptions.WrappingPreserveSingleLine, formattingOptions.WrappingPreserveSingleLine) .WithChangedOption(CSharpFormattingOptions.WrappingKeepStatementsOnSingleLine, formattingOptions.WrappingKeepStatementsOnSingleLine) .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInTypes, formattingOptions.NewLinesForBracesInTypes) @@ -70,32 +70,6 @@ private static OptionSet GetOptions(OptionSet optionSet, FormattingOptions forma .WithChangedOption(CSharpFormattingOptions.NewLineForClausesInQuery, formattingOptions.NewLineForClausesInQuery); } - private static LabelPositionOptions LabelPositionOptionForStringValue(string value) - { - switch (value.ToUpper()) - { - case "LEFTMOST": - return LabelPositionOptions.LeftMost; - case "NOINDENT": - return LabelPositionOptions.NoIndent; - default: - return LabelPositionOptions.OneLess; - } - } - - private static BinaryOperatorSpacingOptions BinaryOperatorSpacingOptionForStringValue(string value) - { - switch (value.ToUpper()) - { - case "IGNORE": - return BinaryOperatorSpacingOptions.Ignore; - case "REMOVE": - return BinaryOperatorSpacingOptions.Remove; - default: - return BinaryOperatorSpacingOptions.Single; - } - } - public OptionSet Process(OptionSet currentOptionSet, OmniSharpOptions options, IOmniSharpEnvironment omnisharpEnvironment) { return GetOptions(currentOptionSet, options.FormattingOptions); diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs index 2ce0129c28..1c449f4556 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs @@ -85,7 +85,7 @@ public async Task Handle(CompletionRequest request) return new CompletionResponse { Items = ImmutableArray.Empty }; } - var (completions, expandedItemsAvailable) = await OmniSharpCompletionService.GetCompletionsAsync(completionService, document, position, trigger, roles: null, options, CancellationToken.None); + var completions = await OmniSharpCompletionService.GetCompletionsAsync(completionService, document, position, trigger, roles: null, options, CancellationToken.None); _logger.LogTrace("Found {0} completions for {1}:{2},{3}", completions?.Items.IsDefaultOrEmpty != false ? 0 : completions.Items.Length, request.FileName, @@ -119,7 +119,7 @@ CompletionListBuilder.PartialMethodCompletionProvider or // that completion provider is still creating the cache. We'll mark this completion list as not completed, and the // editor will ask again when the user types more. By then, hopefully the cache will have populated and we can mark // the completion as done. - bool expectingImportedItems = expandedItemsAvailable && _omniSharpOptions.RoslynExtensionsOptions.EnableImportCompletion; + bool expectingImportedItems = options.ShowItemsFromUnimportedNamespaces; var syntax = await document.GetSyntaxTreeAsync(); var replacingSpanStartPosition = sourceText.Lines.GetLinePosition(typedSpan.Start); diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Decompilation/DecompilationExternalSourceService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Decompilation/DecompilationExternalSourceService.cs index c31013abee..8c25a2b2b0 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Decompilation/DecompilationExternalSourceService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Decompilation/DecompilationExternalSourceService.cs @@ -1,6 +1,7 @@ using Microsoft.CodeAnalysis; using Microsoft.Extensions.Logging; using OmniSharp.Extensions; +using OmniSharp.Options; using System; using System.Composition; using System.Linq; @@ -17,10 +18,10 @@ public class DecompilationExternalSourceService : BaseExternalSourceService, IEx private readonly Lazy _service; [ImportingConstructor] - public DecompilationExternalSourceService(ILoggerFactory loggerFactory) : base() + public DecompilationExternalSourceService(ILoggerFactory loggerFactory, OmniSharpOptions omnisharpOptions) : base() { _loggerFactory = loggerFactory; - _service = new Lazy(() => new OmniSharpCSharpDecompiledSourceService(_loggerFactory)); + _service = new Lazy(() => new OmniSharpCSharpDecompiledSourceService(omnisharpOptions, _loggerFactory)); } public async Task<(Document document, string documentPath)> GetAndAddExternalSymbolDocument(Project project, ISymbol symbol, CancellationToken cancellationToken) diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Decompilation/OmniSharpCSharpDecompiledSourceService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Decompilation/OmniSharpCSharpDecompiledSourceService.cs index dc93bb90c2..fb0cf80c5f 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Decompilation/OmniSharpCSharpDecompiledSourceService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Decompilation/OmniSharpCSharpDecompiledSourceService.cs @@ -19,18 +19,23 @@ using OmniSharp.Extensions; using Microsoft.CodeAnalysis; using Microsoft.Extensions.Logging; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.MetadataAsSource; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CSharp.DocumentationComments; +using OmniSharp.Options; +using OmniSharp.Roslyn.CSharp.Workers.Formatting; namespace OmniSharp.Roslyn.CSharp.Services.Decompilation { public class OmniSharpCSharpDecompiledSourceService { + private readonly OmniSharpOptions _omnisharpOptions; private readonly ILoggerFactory _loggerFactory; private static readonly FileVersionInfo decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location); - public OmniSharpCSharpDecompiledSourceService(ILoggerFactory loggerFactory) + public OmniSharpCSharpDecompiledSourceService(OmniSharpOptions omnisharpOptions, ILoggerFactory loggerFactory) { + _omnisharpOptions = omnisharpOptions; _loggerFactory = loggerFactory; } @@ -58,9 +63,9 @@ public async Task AddSourceToAsync(Document document, Compilation symb var node = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // Apply formatting rules - document = await Formatter.FormatAsync( - document, new[] { node.FullSpan }, - options: null, cancellationToken).ConfigureAwait(false); + + var options = await FormattingWorker.GetFormattingOptionsAsync(document, _omnisharpOptions).ConfigureAwait(false); + document = await OmniSharpFormatter.FormatAsync(document, new[] { node.FullSpan }, options, cancellationToken).ConfigureAwait(false); return document; } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Formatting/CodeFormatService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Formatting/CodeFormatService.cs index 4060b0a258..89e535291d 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Formatting/CodeFormatService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Formatting/CodeFormatService.cs @@ -14,14 +14,12 @@ public class CodeFormatService : IRequestHandler Handle(CodeFormatRequest request) @@ -34,14 +32,14 @@ public async Task Handle(CodeFormatRequest request) if (request.WantsTextChanges) { - var textChanges = await FormattingWorker.GetFormattedTextChanges(document, _omnisharpOptions, _loggerFactory); + var textChanges = await FormattingWorker.GetFormattedTextChanges(document, _omnisharpOptions); return new CodeFormatResponse() { Changes = textChanges }; } - var newText = await FormattingWorker.GetFormattedText(document, _omnisharpOptions, _loggerFactory); + var newText = await FormattingWorker.GetFormattedText(document, _omnisharpOptions); return new CodeFormatResponse { diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Formatting/FormatAfterKeystrokeService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Formatting/FormatAfterKeystrokeService.cs index c62949707d..a71d1a88ee 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Formatting/FormatAfterKeystrokeService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Formatting/FormatAfterKeystrokeService.cs @@ -17,14 +17,12 @@ public class FormatAfterKeystrokeService : IRequestHandler Handle(FormatAfterKeystrokeRequest request) @@ -37,7 +35,7 @@ public async Task Handle(FormatAfterKeystrokeRequest reques var text = await document.GetTextAsync(); int position = text.GetTextPosition(request); - var changes = await FormattingWorker.GetFormattingChangesAfterKeystroke(document, position, request.Char, _omnisharpOptions, _loggerFactory); + var changes = await FormattingWorker.GetFormattingChangesAfterKeystroke(document, position, request.Char, _omnisharpOptions); return new FormatRangeResponse() { diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Formatting/FormatRangeService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Formatting/FormatRangeService.cs index 07493e24dd..0f880383c3 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Formatting/FormatRangeService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Formatting/FormatRangeService.cs @@ -16,14 +16,12 @@ public class FormatRangeService : IRequestHandler Handle(FormatRangeRequest request) @@ -39,7 +37,7 @@ public async Task Handle(FormatRangeRequest request) var end = text.Lines.GetPosition(new LinePosition(request.EndLine, request.EndColumn)); var syntaxTree = await document.GetSyntaxRootAsync(); var tokenStart = syntaxTree.FindToken(start).FullSpan.Start; - var changes = await FormattingWorker.GetFormattingChanges(document, tokenStart, end, _omnisharpOptions, _loggerFactory); + var changes = await FormattingWorker.GetFormattingChanges(document, tokenStart, end, _omnisharpOptions); return new FormatRangeResponse() { diff --git a/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs b/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs deleted file mode 100644 index 720ff23c5e..0000000000 --- a/src/OmniSharp.Roslyn.CSharp/Services/ImplementTypeWorkspaceOptionsProvider.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Composition; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ImplementType; -using Microsoft.CodeAnalysis.Options; -using OmniSharp.Options; -using OmniSharp.Roslyn.Options; - -namespace OmniSharp.Roslyn.CSharp.Services -{ - [Export(typeof(IWorkspaceOptionsProvider)), Shared] - public class ImplementTypeWorkspaceOptionsProvider : IWorkspaceOptionsProvider - { - [ImportingConstructor] - public ImplementTypeWorkspaceOptionsProvider() - { - } - - public int Order => 110; - - public OptionSet Process(OptionSet currentOptionSet, OmniSharpOptions omniSharpOptions, IOmniSharpEnvironment omnisharpEnvironment) - { - if (omniSharpOptions.ImplementTypeOptions.InsertionBehavior != null) - { - currentOptionSet = OmniSharpImplementTypeOptions.SetInsertionBehavior(currentOptionSet, LanguageNames.CSharp, (OmniSharpImplementTypeInsertionBehavior)omniSharpOptions.ImplementTypeOptions.InsertionBehavior); - } - - if (omniSharpOptions.ImplementTypeOptions.PropertyGenerationBehavior != null) - { - currentOptionSet = OmniSharpImplementTypeOptions.SetPropertyGenerationBehavior(currentOptionSet, LanguageNames.CSharp, (OmniSharpImplementTypePropertyGenerationBehavior)omniSharpOptions.ImplementTypeOptions.PropertyGenerationBehavior); - } - - return currentOptionSet; - } - } -} diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs index adbcfec43a..29141125a4 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs @@ -49,7 +49,7 @@ public async Task> Handle(AutoCompleteRequest var position = sourceText.GetTextPosition(request); var service = CompletionService.GetService(document); var options = new OmniSharpCompletionOptions(ShowItemsFromUnimportedNamespaces: _omniSharpOptions.RoslynExtensionsOptions.EnableImportCompletion); - var (completionList, expandedItemsAvailable) = await OmniSharpCompletionService.GetCompletionsAsync(service, document, position, trigger: default, roles: null, options, CancellationToken.None); + var completionList = await OmniSharpCompletionService.GetCompletionsAsync(service, document, position, trigger: default, roles: null, options, CancellationToken.None); if (completionList != null) { diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/FixUsingService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/FixUsingService.cs index 0c2fafb766..eee336d7fe 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/FixUsingService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/FixUsingService.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging; using OmniSharp.Mef; using OmniSharp.Models.FixUsings; +using OmniSharp.Options; using OmniSharp.Roslyn.Utilities; using OmniSharp.Services; @@ -14,17 +15,17 @@ namespace OmniSharp.Roslyn.CSharp.Services.Refactoring public class FixUsingService : IRequestHandler { private readonly OmniSharpWorkspace _workspace; - private readonly ILoggerFactory _loggerFactory; + private readonly OmniSharpOptions _options; private readonly IEnumerable _providers; [ImportingConstructor] public FixUsingService( OmniSharpWorkspace workspace, - ILoggerFactory loggerFactory, + OmniSharpOptions options, [ImportMany] IEnumerable codeActionProviders) { _workspace = workspace; - _loggerFactory = loggerFactory; + _options = options; _providers = codeActionProviders; } @@ -35,7 +36,7 @@ public async Task Handle(FixUsingsRequest request) var oldDocument = _workspace.GetDocument(request.FileName); if (oldDocument != null) { - var fixUsingsResponse = await new FixUsingsWorker(_providers) + var fixUsingsResponse = await new FixUsingsWorker(_providers, _options) .FixUsingsAsync(oldDocument); response.AmbiguousResults = fixUsingsResponse.AmbiguousResults; diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/GetFixAllCodeActionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/GetFixAllCodeActionService.cs index 8c16173561..5893608803 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/GetFixAllCodeActionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/GetFixAllCodeActionService.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging; using OmniSharp.Abstractions.Models.V1.FixAll; using OmniSharp.Mef; +using OmniSharp.Options; using OmniSharp.Roslyn.CSharp.Helpers; using OmniSharp.Roslyn.CSharp.Services.Refactoring.V2; using OmniSharp.Roslyn.CSharp.Workers.Diagnostics; @@ -23,8 +24,9 @@ public GetFixAllCodeActionService( [ImportMany] IEnumerable providers, ILoggerFactory loggerFactory, ICsDiagnosticWorker diagnostics, - CachingCodeFixProviderForProjects codeFixesForProject - ) : base(workspace, providers, loggerFactory.CreateLogger(), diagnostics, codeFixesForProject) + CachingCodeFixProviderForProjects codeFixesForProject, + OmniSharpOptions options + ) : base(workspace, providers, loggerFactory.CreateLogger(), diagnostics, codeFixesForProject, options) { } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RunFixAllCodeActionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RunFixAllCodeActionService.cs index 7b2c168cb6..4a54e5a1ef 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RunFixAllCodeActionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RunFixAllCodeActionService.cs @@ -9,10 +9,13 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.Extensions.Logging; using OmniSharp.Abstractions.Models.V1.FixAll; using OmniSharp.Mef; +using OmniSharp.Options; +using OmniSharp.Roslyn.CodeActions; using OmniSharp.Roslyn.CSharp.Services.Refactoring.V2; using OmniSharp.Roslyn.CSharp.Workers.Diagnostics; using OmniSharp.Services; @@ -32,13 +35,15 @@ public RunFixAllCodeActionService(ICsDiagnosticWorker diagnosticWorker, [ImportMany] IEnumerable providers, CachingCodeFixProviderForProjects codeFixProvider, OmniSharpWorkspace workspace, + OmniSharpOptions options, ILoggerFactory loggerFactory) : base( workspace, providers, loggerFactory.CreateLogger(), diagnosticWorker, - codeFixProvider) + codeFixProvider, + options) { _logger = loggerFactory.CreateLogger(); _fixAllDiagnosticProvider = new FixAllDiagnosticProvider(diagnosticWorker); @@ -134,8 +139,14 @@ private async Task FixSpecificDiagnosticIdAsync(Document document, str } _logger.LogTrace("{0} is still present in the document. Getting fixes.", diagnosticId); + + var codeActionOptions = CodeActionOptionsFactory.Create(Options); + CodeAction action = null; - var context = new CodeFixContext(document, primaryDiagnostic, + var context = OmniSharpCodeFixContextFactory.CreateCodeFixContext( + document, + primaryDiagnostic.Location.SourceSpan, + ImmutableArray.Create(primaryDiagnostic), (a, _) => { if (action == null) @@ -143,6 +154,7 @@ private async Task FixSpecificDiagnosticIdAsync(Document document, str action = a; } }, + codeActionOptions, cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); @@ -155,7 +167,9 @@ private async Task FixSpecificDiagnosticIdAsync(Document document, str _ => throw new InvalidOperationException() }; - var fixAllContext = new FixAllContext(document, codeFixProvider, roslynScope, action.EquivalenceKey, ImmutableArray.Create(diagnosticId), _fixAllDiagnosticProvider, cancellationToken); + var fixAllContext = OmniSharpCodeFixContextFactory.CreateFixAllContext( + document, document.Project, codeFixProvider, roslynScope, action.EquivalenceKey, ImmutableArray.Create(diagnosticId), _fixAllDiagnosticProvider, + _ => codeActionOptions, cancellationToken); _logger.LogTrace("Finding FixAll fix for {0}.", diagnosticId); var fixes = await fixAllProvider.GetFixAsync(fixAllContext); diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/BaseCodeActionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/BaseCodeActionService.cs index 57380409ad..6801c82b9c 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/BaseCodeActionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/BaseCodeActionService.cs @@ -12,12 +12,15 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CodeActions; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ImplementType; using Microsoft.CodeAnalysis.Text; using Microsoft.Extensions.Logging; using OmniSharp.Extensions; using OmniSharp.Mef; using OmniSharp.Models; using OmniSharp.Models.V2.CodeActions; +using OmniSharp.Options; +using OmniSharp.Roslyn.CodeActions; using OmniSharp.Roslyn.CSharp.Helpers; using OmniSharp.Roslyn.CSharp.Services.Diagnostics; using OmniSharp.Roslyn.CSharp.Workers.Diagnostics; @@ -35,6 +38,7 @@ public abstract class BaseCodeActionService : IRequestHandl protected readonly OmniSharpWorkspace Workspace; protected readonly IEnumerable Providers; protected readonly ILogger Logger; + protected readonly OmniSharpOptions Options; private readonly ICsDiagnosticWorker _diagnostics; private readonly CachingCodeFixProviderForProjects _codeFixesForProject; @@ -51,11 +55,13 @@ protected BaseCodeActionService( IEnumerable providers, ILogger logger, ICsDiagnosticWorker diagnostics, - CachingCodeFixProviderForProjects codeFixesForProject) + CachingCodeFixProviderForProjects codeFixesForProject, + OmniSharpOptions options) { Workspace = workspace; Providers = providers; Logger = logger; + Options = options; _diagnostics = diagnostics; _codeFixesForProject = codeFixesForProject; OrderedCodeRefactoringProviders = new Lazy>(() => GetSortedCodeRefactoringProviders()); @@ -131,13 +137,21 @@ private async Task CollectCodeFixesActions(Document document, TextSpan span, Lis private async Task AppendFixesAsync(Document document, TextSpan span, IEnumerable diagnostics, List codeActions) { + var codeActionOptions = CodeActionOptionsFactory.Create(Options); + foreach (var codeFixProvider in GetSortedCodeFixProviders(document)) { var fixableDiagnostics = diagnostics.Where(d => HasFix(codeFixProvider, d.Id)).ToImmutableArray(); if (fixableDiagnostics.Length > 0) { - var context = new CodeFixContext(document, span, fixableDiagnostics, (a, _) => codeActions.Add(a), CancellationToken.None); + var context = OmniSharpCodeFixContextFactory.CreateCodeFixContext( + document, + span, + fixableDiagnostics, + (a, _) => codeActions.Add(a), + codeActionOptions, + CancellationToken.None); try { diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/GetCodeActionsService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/GetCodeActionsService.cs index 57893a5792..dafa236f62 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/GetCodeActionsService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/GetCodeActionsService.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging; using OmniSharp.Mef; using OmniSharp.Models.V2.CodeActions; +using OmniSharp.Options; using OmniSharp.Roslyn.CSharp.Services.CodeActions; using OmniSharp.Roslyn.CSharp.Workers.Diagnostics; using OmniSharp.Services; @@ -22,8 +23,9 @@ public GetCodeActionsService( [ImportMany] IEnumerable providers, ILoggerFactory loggerFactory, ICsDiagnosticWorker diagnostics, - CachingCodeFixProviderForProjects codeFixesForProjects) - : base(workspace, providers, loggerFactory.CreateLogger(), diagnostics, codeFixesForProjects) + CachingCodeFixProviderForProjects codeFixesForProjects, + OmniSharpOptions options) + : base(workspace, providers, loggerFactory.CreateLogger(), diagnostics, codeFixesForProjects, options) { } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs index 9a362910ee..ad9c2b2c05 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/V2/RunCodeActionService.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging; using OmniSharp.Mef; using OmniSharp.Models; +using OmniSharp.Options; using OmniSharp.Roslyn.CSharp.Services.CodeActions; using OmniSharp.Roslyn.CSharp.Workers.Diagnostics; using OmniSharp.Services; @@ -33,8 +34,9 @@ public RunCodeActionService( [ImportMany] IEnumerable providers, ILoggerFactory loggerFactory, ICsDiagnosticWorker diagnostics, - CachingCodeFixProviderForProjects codeFixesForProjects) - : base(workspace, providers, loggerFactory.CreateLogger(), diagnostics, codeFixesForProjects) + CachingCodeFixProviderForProjects codeFixesForProjects, + OmniSharpOptions options) + : base(workspace, providers, loggerFactory.CreateLogger(), diagnostics, codeFixesForProjects, options) { _loader = loader; _workspaceAssembly = _loader.LazyLoad(Configuration.RoslynWorkspaces); diff --git a/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticWorkerWithAnalyzers.cs b/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticWorkerWithAnalyzers.cs index 7b34d29a01..b04cdac505 100644 --- a/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticWorkerWithAnalyzers.cs +++ b/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticWorkerWithAnalyzers.cs @@ -9,6 +9,8 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Analyzers; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ImplementType; using Microsoft.Extensions.Logging; using OmniSharp.Helpers; using OmniSharp.Models.Diagnostics; @@ -31,11 +33,6 @@ public class CSharpDiagnosticWorkerWithAnalyzers : ICsDiagnosticWorker, IDisposa private readonly OmniSharpOptions _options; private readonly OmniSharpWorkspace _workspace; - // This is workaround. - // Currently roslyn doesn't expose official way to use IDE analyzers during analysis. - // This options gives certain IDE analysis access for services that are not yet publicly available. - private readonly ConstructorInfo _workspaceAnalyzerOptionsConstructor; - public CSharpDiagnosticWorkerWithAnalyzers( OmniSharpWorkspace workspace, [ImportMany] IEnumerable providers, @@ -52,12 +49,6 @@ public CSharpDiagnosticWorkerWithAnalyzers( _options = options; _workspace = workspace; - _workspaceAnalyzerOptionsConstructor = Assembly - .Load("Microsoft.CodeAnalysis.Features") - .GetType("Microsoft.CodeAnalysis.Diagnostics.WorkspaceAnalyzerOptions") - .GetConstructor(new Type[] { typeof(AnalyzerOptions), typeof(Solution) }) - ?? throw new InvalidOperationException("Could not resolve 'Microsoft.CodeAnalysis.Diagnostics.WorkspaceAnalyzerOptions' for IDE analyzers."); - _workspace.WorkspaceChanged += OnWorkspaceChanged; _workspace.OnInitialized += OnWorkspaceInitialized; @@ -220,22 +211,24 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs changeEv } } + private AnalyzerOptions CreateAnalyzerOptions(Project project) + => OmniSharpWorkspaceAnalyzerOptionsFactory.Create(project.Solution, project.AnalyzerOptions); + public async Task> AnalyzeDocumentAsync(Document document, CancellationToken cancellationToken) { Project project = document.Project; var allAnalyzers = GetAnalyzersForProject(project); var compilation = await project.GetCompilationAsync(cancellationToken); - var workspaceAnalyzerOptions = (AnalyzerOptions)_workspaceAnalyzerOptionsConstructor.Invoke(new object[] { project.AnalyzerOptions, project.Solution }); - + cancellationToken.ThrowIfCancellationRequested(); - return await AnalyzeDocument(project, allAnalyzers, compilation, workspaceAnalyzerOptions, document); + return await AnalyzeDocument(project, allAnalyzers, compilation, CreateAnalyzerOptions(document.Project), document); } public async Task> AnalyzeProjectsAsync(Project project, CancellationToken cancellationToken) { var allAnalyzers = GetAnalyzersForProject(project); var compilation = await project.GetCompilationAsync(cancellationToken); - var workspaceAnalyzerOptions = (AnalyzerOptions)_workspaceAnalyzerOptionsConstructor.Invoke(new object[] { project.AnalyzerOptions, project.Solution }); + var workspaceAnalyzerOptions = CreateAnalyzerOptions(project); var documentAnalyzerTasks = new List(); var diagnostics = ImmutableList.Empty; @@ -269,7 +262,7 @@ private async Task AnalyzeProject(Solution solution, IGrouping(); foreach (var documentId in documentsGroupedByProject) diff --git a/src/OmniSharp.Roslyn.CSharp/Workers/Formatting/FormattingWorker.cs b/src/OmniSharp.Roslyn.CSharp/Workers/Formatting/FormattingWorker.cs index 929f709104..b35e7adeb8 100644 --- a/src/OmniSharp.Roslyn.CSharp/Workers/Formatting/FormattingWorker.cs +++ b/src/OmniSharp.Roslyn.CSharp/Workers/Formatting/FormattingWorker.cs @@ -5,9 +5,12 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using ICSharpCode.Decompiler.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CSharp.Formatting; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.DocumentationComments; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Text; using Microsoft.Extensions.Logging; @@ -20,7 +23,7 @@ namespace OmniSharp.Roslyn.CSharp.Workers.Formatting { public static class FormattingWorker { - public static async Task> GetFormattingChangesAfterKeystroke(Document document, int position, char character, OmniSharpOptions omnisharpOptions, ILoggerFactory loggerFactory) + public static async Task> GetFormattingChangesAfterKeystroke(Document document, int position, char character, OmniSharpOptions omnisharpOptions) { if (await GetDocumentationCommentChanges(document, position, character, omnisharpOptions) is LinePositionSpanTextChange change) { @@ -35,7 +38,7 @@ public static async Task> GetFormattingC var node = FindFormatTarget(root!, position); if (node != null) { - return await GetFormattingChanges(document, node.FullSpan.Start, node.FullSpan.End, omnisharpOptions, loggerFactory); + return await GetFormattingChanges(document, node.FullSpan.Start, node.FullSpan.End, omnisharpOptions); } } @@ -79,41 +82,140 @@ public static async Task> GetFormattingC return null; } - public static async Task> GetFormattingChanges(Document document, int start, int end, OmniSharpOptions omnisharpOptions, ILoggerFactory loggerFactory) + public static async Task> GetFormattingChanges(Document document, int start, int end, OmniSharpOptions omnisharpOptions) { - var newDocument = await FormatDocument(document, omnisharpOptions, loggerFactory, TextSpan.FromBounds(start, end)); + var newDocument = await FormatDocument(document, omnisharpOptions, TextSpan.FromBounds(start, end)); return await TextChanges.GetAsync(newDocument, document); } - public static async Task GetFormattedText(Document document, OmniSharpOptions omnisharpOptions, ILoggerFactory loggerFactory) + public static async Task GetFormattedText(Document document, OmniSharpOptions omnisharpOptions) { - var newDocument = await FormatDocument(document, omnisharpOptions, loggerFactory); + var newDocument = await FormatDocument(document, omnisharpOptions); var text = await newDocument.GetTextAsync(); return text.ToString(); } - public static async Task> GetFormattedTextChanges(Document document, OmniSharpOptions omnisharpOptions, ILoggerFactory loggerFactory) + public static async Task> GetFormattedTextChanges(Document document, OmniSharpOptions omnisharpOptions) { - var newDocument = await FormatDocument(document, omnisharpOptions, loggerFactory); + var newDocument = await FormatDocument(document, omnisharpOptions); return await TextChanges.GetAsync(newDocument, document); } - private static async Task FormatDocument(Document document, OmniSharpOptions omnisharpOptions, ILoggerFactory loggerFactory, TextSpan? textSpan = null) + private static async Task FormatDocument(Document document, OmniSharpOptions omnisharpOptions, TextSpan? textSpan = null) { - // If we are not using .editorconfig for formatting options then we can avoid any overhead of calculating document options. - var optionSet = omnisharpOptions.FormattingOptions.EnableEditorConfigSupport - ? await document.GetOptionsAsync() - : document.Project.Solution.Options; - - var newDocument = textSpan != null ? await Formatter.FormatAsync(document, textSpan.Value, optionSet) : await Formatter.FormatAsync(document, optionSet); + var spans = (textSpan != null) ? new[] { textSpan.Value } : null; + var formattingOtions = await GetFormattingOptionsAsync(document, omnisharpOptions); + var newDocument = await OmniSharpFormatter.FormatAsync(document, spans, formattingOtions, CancellationToken.None); if (omnisharpOptions.FormattingOptions.OrganizeImports) { - newDocument = await Formatter.OrganizeImportsAsync(newDocument); + var organizeImportsOptions = await GetOrganizeImportsOptionsAsync(document, omnisharpOptions); + newDocument = await OmniSharpFormatter.OrganizeImportsAsync(newDocument, organizeImportsOptions, CancellationToken.None); } return newDocument; } + // If we are not using .editorconfig for formatting options then we can avoid any overhead of calculating document options. + internal static async ValueTask GetOrganizeImportsOptionsAsync(Document document, OmniSharpOptions omnisharpOptions) + => omnisharpOptions.FormattingOptions.EnableEditorConfigSupport + ? await OmniSharpOrganizeImportsOptionsWrapper.FromDocumentAsync(document, CancellationToken.None) + : WrapOrganizeImportsOptions(omnisharpOptions.FormattingOptions); + + // If we are not using .editorconfig for formatting options then we can avoid any overhead of calculating document options. + internal static async ValueTask GetFormattingOptionsAsync(Document document, OmniSharpOptions omnisharpOptions) + => omnisharpOptions.FormattingOptions.EnableEditorConfigSupport + ? await OmniSharpSyntaxFormattingOptionsWrapper.FromDocumentAsync(document, CancellationToken.None) + : WrapFormattingOptions(omnisharpOptions.FormattingOptions); + + // If we are not using .editorconfig for formatting options then we can avoid any overhead of calculating document options. + internal static async ValueTask GetDocumentationCommentOptionsAsync(Document document, OmniSharpOptions omnisharpOptions) + => omnisharpOptions.FormattingOptions.EnableEditorConfigSupport + ? await OmniSharpDocumentationCommentOptionsWrapper.FromDocumentAsync(document, autoXmlDocCommentGeneration: true, CancellationToken.None) + : WrapDocumentationCommentOptions(omnisharpOptions.FormattingOptions); + + private static OmniSharpOrganizeImportsOptionsWrapper WrapOrganizeImportsOptions(OmniSharp.Options.FormattingOptions options) + => new( + placeSystemNamespaceFirst: true, + separateImportDirectiveGroups: false, + options.NewLine); + + private static OmniSharpSyntaxFormattingOptionsWrapper WrapFormattingOptions(OmniSharp.Options.FormattingOptions options) + => OmniSharpSyntaxFormattingOptionsFactory.Create( + useTabs: options.UseTabs, + tabSize: options.TabSize, + indentationSize: options.IndentationSize, + newLine: options.NewLine, + separateImportDirectiveGroups: options.SeparateImportDirectiveGroups, + spacingAfterMethodDeclarationName: options.SpacingAfterMethodDeclarationName, + spaceWithinMethodDeclarationParenthesis: options.SpaceWithinMethodDeclarationParenthesis, + spaceBetweenEmptyMethodDeclarationParentheses: options.SpaceBetweenEmptyMethodDeclarationParentheses, + spaceAfterMethodCallName: options.SpaceAfterMethodCallName, + spaceWithinMethodCallParentheses: options.SpaceWithinMethodCallParentheses, + spaceBetweenEmptyMethodCallParentheses: options.SpaceBetweenEmptyMethodCallParentheses, + spaceAfterControlFlowStatementKeyword: options.SpaceAfterControlFlowStatementKeyword, + spaceWithinExpressionParentheses: options.SpaceWithinExpressionParentheses, + spaceWithinCastParentheses: options.SpaceWithinCastParentheses, + spaceWithinOtherParentheses: options.SpaceWithinOtherParentheses, + spaceAfterCast: options.SpaceAfterCast, + spaceBeforeOpenSquareBracket: options.SpaceBeforeOpenSquareBracket, + spaceBetweenEmptySquareBrackets: options.SpaceBetweenEmptySquareBrackets, + spaceWithinSquareBrackets: options.SpaceWithinSquareBrackets, + spaceAfterColonInBaseTypeDeclaration: options.SpaceAfterColonInBaseTypeDeclaration, + spaceAfterComma: options.SpaceAfterComma, + spaceAfterDot: options.SpaceAfterDot, + spaceAfterSemicolonsInForStatement: options.SpaceAfterSemicolonsInForStatement, + spaceBeforeColonInBaseTypeDeclaration: options.SpaceBeforeColonInBaseTypeDeclaration, + spaceBeforeComma: options.SpaceBeforeComma, + spaceBeforeDot: options.SpaceBeforeDot, + spaceBeforeSemicolonsInForStatement: options.SpaceBeforeSemicolonsInForStatement, + spacingAroundBinaryOperator: BinaryOperatorSpacingOptionForStringValue(options.SpacingAroundBinaryOperator), + indentBraces: options.IndentBraces, + indentBlock: options.IndentBlock, + indentSwitchSection: options.IndentSwitchSection, + indentSwitchCaseSection: options.IndentSwitchCaseSection, + indentSwitchCaseSectionWhenBlock: options.IndentSwitchCaseSectionWhenBlock, + labelPositioning: LabelPositionOptionForStringValue(options.LabelPositioning), + wrappingPreserveSingleLine: options.WrappingPreserveSingleLine, + wrappingKeepStatementsOnSingleLine: options.WrappingKeepStatementsOnSingleLine, + newLinesForBracesInTypes: options.NewLinesForBracesInTypes, + newLinesForBracesInMethods: options.NewLinesForBracesInMethods, + newLinesForBracesInProperties: options.NewLinesForBracesInProperties, + newLinesForBracesInAccessors: options.NewLinesForBracesInAccessors, + newLinesForBracesInAnonymousMethods: options.NewLinesForBracesInAnonymousMethods, + newLinesForBracesInControlBlocks: options.NewLinesForBracesInControlBlocks, + newLinesForBracesInAnonymousTypes: options.NewLinesForBracesInAnonymousTypes, + newLinesForBracesInObjectCollectionArrayInitializers: options.NewLinesForBracesInObjectCollectionArrayInitializers, + newLinesForBracesInLambdaExpressionBody: options.NewLinesForBracesInLambdaExpressionBody, + newLineForElse: options.NewLineForElse, + newLineForCatch: options.NewLineForCatch, + newLineForFinally: options.NewLineForFinally, + newLineForMembersInObjectInit: options.NewLineForMembersInObjectInit, + newLineForMembersInAnonymousTypes: options.NewLineForMembersInAnonymousTypes, + newLineForClausesInQuery: options.NewLineForClausesInQuery); + + private static OmniSharpDocumentationCommentOptionsWrapper WrapDocumentationCommentOptions(OmniSharp.Options.FormattingOptions options) + => new( + autoXmlDocCommentGeneration: true, + tabSize: options.TabSize, + useTabs: options.UseTabs, + newLine: options.NewLine); + + internal static OmniSharpLabelPositionOptions LabelPositionOptionForStringValue(string value) + => value.ToUpper() switch + { + "LEFTMOST" => OmniSharpLabelPositionOptions.LeftMost, + "NOINDENT" => OmniSharpLabelPositionOptions.NoIndent, + _ => OmniSharpLabelPositionOptions.OneLess, + }; + + internal static OmniSharpBinaryOperatorSpacingOptions BinaryOperatorSpacingOptionForStringValue(string value) + => value.ToUpper() switch + { + "IGNORE" => OmniSharpBinaryOperatorSpacingOptions.Ignore, + "REMOVE" => OmniSharpBinaryOperatorSpacingOptions.Remove, + _ => OmniSharpBinaryOperatorSpacingOptions.Single, + }; + private static async Task GetDocumentationCommentChanges(Document document, int position, char character, OmniSharpOptions omnisharpOptions) { if (character != '\n' && character != '/') @@ -124,11 +226,11 @@ private static async Task FormatDocument(Document document, OmniSharpO var text = await document.GetTextAsync(); var syntaxTree = await document.GetSyntaxTreeAsync(); - var optionSet = await document.GetOptionsAsync(); + var docCommentOptions = await GetDocumentationCommentOptionsAsync(document, omnisharpOptions).ConfigureAwait(false); var snippet = character == '\n' ? - OmniSharpDocumentationCommentsSnippetService.GetDocumentationCommentSnippetOnEnterTyped(document, syntaxTree!, text, position, optionSet, CancellationToken.None) : - OmniSharpDocumentationCommentsSnippetService.GetDocumentationCommentSnippetOnCharacterTyped(document, syntaxTree!, text, position, optionSet, CancellationToken.None); + OmniSharpDocumentationCommentsSnippetService.GetDocumentationCommentSnippetOnEnterTyped(document, syntaxTree!, text, position, docCommentOptions, CancellationToken.None) : + OmniSharpDocumentationCommentsSnippetService.GetDocumentationCommentSnippetOnCharacterTyped(document, syntaxTree!, text, position, docCommentOptions, CancellationToken.None); if (snippet == null) { diff --git a/src/OmniSharp.Roslyn.CSharp/Workers/Refactoring/FixUsingsWorker.cs b/src/OmniSharp.Roslyn.CSharp/Workers/Refactoring/FixUsingsWorker.cs index d64b17a659..c92d524a5f 100644 --- a/src/OmniSharp.Roslyn.CSharp/Workers/Refactoring/FixUsingsWorker.cs +++ b/src/OmniSharp.Roslyn.CSharp/Workers/Refactoring/FixUsingsWorker.cs @@ -9,8 +9,11 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CodeActions; using Microsoft.CodeAnalysis.Text; using OmniSharp.Models; +using OmniSharp.Options; +using OmniSharp.Roslyn.CodeActions; using OmniSharp.Roslyn.CSharp.Services.CodeActions; using OmniSharp.Services; @@ -27,8 +30,9 @@ public class FixUsingsWorker private readonly IEnumerable _providers; private readonly CodeFixProvider _addImportProvider; private readonly CodeFixProvider _removeUnnecessaryUsingsProvider; + private readonly OmniSharpOptions _options; - public FixUsingsWorker(IEnumerable providers) + public FixUsingsWorker(IEnumerable providers, OmniSharpOptions options) { _providers = providers; @@ -36,6 +40,7 @@ public FixUsingsWorker(IEnumerable providers) _addImportProvider = FindCodeFixProviderByTypeFullName(codeFixProviders, CodeActionHelper.AddImportProviderName); _removeUnnecessaryUsingsProvider = FindCodeFixProviderByTypeFullName(codeFixProviders, CodeActionHelper.RemoveUnnecessaryUsingsProviderName); + _options = options; } private static CodeFixProvider FindCodeFixProviderByTypeFullName(IEnumerable providers, string fullName) @@ -238,9 +243,10 @@ private async Task> GetCodeFixOperationsAsyn ImmutableArray diagnostics) { var codeFixes = new List(); - var context = new CodeFixContext( + var context = OmniSharpCodeFixContextFactory.CreateCodeFixContext( document, span, diagnostics, registerCodeFix: (a, d) => codeFixes.Add(a), + CodeActionOptionsFactory.Create(_options), cancellationToken: CancellationToken.None); // Note: We're intentionally not checking CodeFixProvider.FixableDiagnosticIds here. diff --git a/src/OmniSharp.Roslyn/Utilities/CodeActionOptionsFactory.cs b/src/OmniSharp.Roslyn/Utilities/CodeActionOptionsFactory.cs new file mode 100644 index 0000000000..43a1ff2854 --- /dev/null +++ b/src/OmniSharp.Roslyn/Utilities/CodeActionOptionsFactory.cs @@ -0,0 +1,14 @@ +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CodeActions; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ImplementType; +using OmniSharp.Options; + +namespace OmniSharp.Roslyn.CodeActions +{ + internal static class CodeActionOptionsFactory + { + public static OmniSharpCodeActionOptions Create(OmniSharpOptions options) + => new(new OmniSharpImplementTypeOptions( + (OmniSharpImplementTypeInsertionBehavior)options.ImplementTypeOptions.InsertionBehavior, + (OmniSharpImplementTypePropertyGenerationBehavior)options.ImplementTypeOptions.PropertyGenerationBehavior)); + } +} diff --git a/src/OmniSharp.Shared/Options/FormattingOptions.cs b/src/OmniSharp.Shared/Options/FormattingOptions.cs index c95ccd0f32..670b3d6942 100644 --- a/src/OmniSharp.Shared/Options/FormattingOptions.cs +++ b/src/OmniSharp.Shared/Options/FormattingOptions.cs @@ -21,7 +21,6 @@ public FormattingOptions() SpaceWithinCastParentheses = false; SpaceWithinOtherParentheses = false; SpaceAfterCast = false; - SpacesIgnoreAroundVariableDeclaration = false; SpaceBeforeOpenSquareBracket = false; SpaceBetweenEmptySquareBrackets = false; SpaceWithinSquareBrackets = false; @@ -73,6 +72,8 @@ public FormattingOptions() public bool SpacingAfterMethodDeclarationName { get; set; } + public bool SeparateImportDirectiveGroups { get; set; } + public bool SpaceWithinMethodDeclarationParenthesis { get; set; } public bool SpaceBetweenEmptyMethodDeclarationParentheses { get; set; } @@ -93,8 +94,6 @@ public FormattingOptions() public bool SpaceAfterCast { get; set; } - public bool SpacesIgnoreAroundVariableDeclaration { get; set; } - public bool SpaceBeforeOpenSquareBracket { get; set; } public bool SpaceBetweenEmptySquareBrackets { get; set; } diff --git a/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs b/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs index 7a609ebc1e..0e58fa4084 100644 --- a/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs +++ b/src/OmniSharp.Shared/Options/ImplementTypeOptions.cs @@ -2,8 +2,8 @@ { public class ImplementTypeOptions { - public ImplementTypeInsertionBehavior? InsertionBehavior { get; set; } - public ImplementTypePropertyGenerationBehavior? PropertyGenerationBehavior { get; set; } + public ImplementTypeInsertionBehavior InsertionBehavior { get; set; } + public ImplementTypePropertyGenerationBehavior PropertyGenerationBehavior { get; set; } } public enum ImplementTypeInsertionBehavior diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/FormatAfterKeystrokeTests.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/FormatAfterKeystrokeTests.cs index e87ca36993..aff4b3ad34 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/FormatAfterKeystrokeTests.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/FormatAfterKeystrokeTests.cs @@ -391,7 +391,8 @@ private async Task VerifyChange(string fileName, string typedCharacter, string o private async Task<(FormatRangeResponse, TestFile)> GetResponse(string text, string character, string fileName) { - // Ensure system newlines are used + // Ensure system newlines are used when editorconfig is used but the config does not specify new line settings. + // This will be removed in future once Roslyn formatting APIs allow to specify defaults for editorconfig settings explicitly. var options = SharedOmniSharpTestHost.Workspace.Options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, System.Environment.NewLine); SharedOmniSharpTestHost.Workspace.TryApplyChanges(SharedOmniSharpTestHost.Workspace.CurrentSolution.WithOptions(options)); diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/FormattingFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/FormattingFacts.cs index 249061e94c..a38a19b39b 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/FormattingFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/FormattingFacts.cs @@ -235,23 +235,18 @@ public async Task FormatRespectsIndentationSize() { var testFile = new TestFile("dummy.cs", "namespace Bar\n{\n class Foo {}\n}"); - using (var host = CreateOmniSharpHost(testFile)) + using var host = CreateOmniSharpHost(new[] { testFile }, new Dictionary { - var optionsProvider = new CSharpFormattingWorkspaceOptionsProvider(); + ["FormattingOptions:NewLine"] = "\n", + ["FormattingOptions:IndentationSize"] = "1" + }); - var omnisharpOptions = new OmniSharpOptions(); - omnisharpOptions.FormattingOptions.NewLine = "\n"; - omnisharpOptions.FormattingOptions.IndentationSize = 1; + var requestHandler = host.GetRequestHandler(OmniSharpEndpoints.CodeFormat); - host.Workspace.TryApplyChanges(host.Workspace.CurrentSolution.WithOptions(optionsProvider.Process(host.Workspace.Options, omnisharpOptions, new OmniSharpEnvironment()))); - - var requestHandler = host.GetRequestHandler(OmniSharpEndpoints.CodeFormat); - - var request = new CodeFormatRequest { FileName = testFile.FileName }; - var response = await requestHandler.Handle(request); + var request = new CodeFormatRequest { FileName = testFile.FileName }; + var response = await requestHandler.Handle(request); - Assert.Equal("namespace Bar\n{\n class Foo { }\n}", response.Buffer); - } + Assert.Equal("namespace Bar\n{\n class Foo { }\n}", response.Buffer); } [Fact] diff --git a/tests/TestUtility/AbstractTestFixture.cs b/tests/TestUtility/AbstractTestFixture.cs index 86759de8c3..9b862ebe11 100644 --- a/tests/TestUtility/AbstractTestFixture.cs +++ b/tests/TestUtility/AbstractTestFixture.cs @@ -1,5 +1,7 @@ +using System; using System.Collections.Generic; using System.Composition.Hosting.Core; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using TestUtility.Logging; using Xunit; @@ -14,7 +16,6 @@ public abstract class AbstractTestFixture : IClassFixture Date: Fri, 25 Feb 2022 10:37:09 -0800 Subject: [PATCH 2/6] FIx --- .../FormatAfterKeystrokeTests.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/FormatAfterKeystrokeTests.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/FormatAfterKeystrokeTests.cs index aff4b3ad34..baa7c18618 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/FormatAfterKeystrokeTests.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/FormatAfterKeystrokeTests.cs @@ -1,5 +1,6 @@ #nullable enable +using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Formatting; @@ -364,6 +365,13 @@ public void M() }"); } + protected override OmniSharpTestHost CreateSharedOmniSharpTestHost() + => CreateOmniSharpHost(configurationData: new Dictionary() + { + ["FormattingOptions:NewLine"] = System.Environment.NewLine + }); + + private async Task VerifyNoChange(string fileName, string typedCharacter, string originalMarkup) { var (response, _) = await GetResponse(originalMarkup, typedCharacter, fileName); @@ -391,11 +399,6 @@ private async Task VerifyChange(string fileName, string typedCharacter, string o private async Task<(FormatRangeResponse, TestFile)> GetResponse(string text, string character, string fileName) { - // Ensure system newlines are used when editorconfig is used but the config does not specify new line settings. - // This will be removed in future once Roslyn formatting APIs allow to specify defaults for editorconfig settings explicitly. - var options = SharedOmniSharpTestHost.Workspace.Options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, System.Environment.NewLine); - SharedOmniSharpTestHost.Workspace.TryApplyChanges(SharedOmniSharpTestHost.Workspace.CurrentSolution.WithOptions(options)); - var file = new TestFile(fileName, text); SharedOmniSharpTestHost.AddFilesToWorkspace(file); var point = file.Content.GetPointFromPosition(); From 43ca31876ec9266f83854b29d4a14245f0e5f3bc Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 7 Mar 2022 18:46:21 -0800 Subject: [PATCH 3/6] Force complete completion results in Completion tests --- build/Packages.props | 2 +- .../Services/Completion/CompletionService.cs | 11 +++++++++-- .../Services/Intellisense/IntellisenseService.cs | 4 +++- .../OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs | 3 ++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/build/Packages.props b/build/Packages.props index 1329e39342..0b65ea624a 100644 --- a/build/Packages.props +++ b/build/Packages.props @@ -7,7 +7,7 @@ 17.0.0 17.0.0 6.0.0 - 4.2.0-2.22124.7 + 4.2.0-3.22157.8 2.4.1 diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs index 1c449f4556..fab7d0fb5a 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs @@ -52,7 +52,12 @@ public CompletionService(OmniSharpWorkspace workspace, FormattingOptions formatt _omniSharpOptions = omniSharpOptions; } - public async Task Handle(CompletionRequest request) + public Task Handle(CompletionRequest request) + { + return Handle(request, forceExpandedCompletionIndexCreation: false); + } + + public async Task Handle(CompletionRequest request, bool forceExpandedCompletionIndexCreation) { _logger.LogTrace("Completions requested"); @@ -76,7 +81,9 @@ public async Task Handle(CompletionRequest request) _ => CompletionTrigger.Invoke, }; - var options = new OmniSharpCompletionOptions(ShowItemsFromUnimportedNamespaces: _omniSharpOptions.RoslynExtensionsOptions.EnableImportCompletion); + var options = new OmniSharpCompletionOptions( + ShowItemsFromUnimportedNamespaces: _omniSharpOptions.RoslynExtensionsOptions.EnableImportCompletion, + ForceExpandedCompletionIndexCreation: forceExpandedCompletionIndexCreation); if (request.CompletionTrigger == CompletionTriggerKind.TriggerCharacter && !await OmniSharpCompletionService.ShouldTriggerCompletionAsync(completionService, document, position, trigger, roles: null, options, CancellationToken.None)) diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs index 29141125a4..14cfc80e01 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Intellisense/IntellisenseService.cs @@ -48,7 +48,9 @@ public async Task> Handle(AutoCompleteRequest var sourceText = await document.GetTextAsync(); var position = sourceText.GetTextPosition(request); var service = CompletionService.GetService(document); - var options = new OmniSharpCompletionOptions(ShowItemsFromUnimportedNamespaces: _omniSharpOptions.RoslynExtensionsOptions.EnableImportCompletion); + var options = new OmniSharpCompletionOptions( + ShowItemsFromUnimportedNamespaces: _omniSharpOptions.RoslynExtensionsOptions.EnableImportCompletion, + ForceExpandedCompletionIndexCreation: false); var completionList = await OmniSharpCompletionService.GetCompletionsAsync(service, document, position, trigger: default, roles: null, options, CancellationToken.None); if (completionList != null) diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs index b54d71d03a..4fd50d16cc 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs @@ -2278,7 +2278,8 @@ protected async Task FindCompletionsAsync(string filename, s var requestHandler = GetCompletionService(testHost); - return await requestHandler.Handle(request); + // Force the completion handler to return a complete set of results. + return await requestHandler.Handle(request, forceExpandedCompletionIndexCreation: true); } private async Task FindCompletionsWithImportedAsync(string filename, string source, OmniSharpTestHost host) From 4cb505a1972df1d7ebec66435a131e94c9f7e161 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 7 Mar 2022 21:36:47 -0800 Subject: [PATCH 4/6] Fix Completion test that does not rely on a fully created index. --- tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs index 4fd50d16cc..2a059f478c 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs @@ -163,7 +163,7 @@ public Class1() using var host = useAsyncCompletion ? GetAsyncCompletionAndImportCompletionHost() : GetImportCompletionHost(); // First completion request should kick off the task to update the completion cache. - var completions = await FindCompletionsAsync(filename, input, host); + var completions = await FindCompletionsAsync(filename, input, host, forceExpandedCompletionIndexCreation: false); Assert.True(completions.IsIncomplete); Assert.DoesNotContain("Guid", completions.Items.Select(c => c.TextEdit.NewText)); Assert.All(completions.Items, c => Assert.False(c.HasAfterInsertStep)); @@ -175,7 +175,7 @@ await Task.Run(async () => { while (completions.IsIncomplete) { - completions = await FindCompletionsAsync(filename, input, host); + completions = await FindCompletionsAsync(filename, input, host, forceExpandedCompletionIndexCreation: false); cts.Token.ThrowIfCancellationRequested(); } }, cts.Token); @@ -2253,7 +2253,7 @@ public C() private CompletionService GetCompletionService(OmniSharpTestHost host) => host.GetRequestHandler(EndpointName); - protected async Task FindCompletionsAsync(string filename, string source, OmniSharpTestHost testHost, char? triggerChar = null, TestFile[] additionalFiles = null) + protected async Task FindCompletionsAsync(string filename, string source, OmniSharpTestHost testHost, char? triggerChar = null, TestFile[] additionalFiles = null, bool forceExpandedCompletionIndexCreation = true) { var testFile = new TestFile(filename, source); @@ -2278,8 +2278,7 @@ protected async Task FindCompletionsAsync(string filename, s var requestHandler = GetCompletionService(testHost); - // Force the completion handler to return a complete set of results. - return await requestHandler.Handle(request, forceExpandedCompletionIndexCreation: true); + return await requestHandler.Handle(request, forceExpandedCompletionIndexCreation); } private async Task FindCompletionsWithImportedAsync(string filename, string source, OmniSharpTestHost host) From 54d294c0a83d1a35576a55e22755209220d4778d Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 7 Mar 2022 23:40:29 -0800 Subject: [PATCH 5/6] Simplify CompletionFacts changes --- .../CompletionFacts.cs | 31 +++---------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs index 2a059f478c..69ffd79ba8 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs @@ -163,7 +163,7 @@ public Class1() using var host = useAsyncCompletion ? GetAsyncCompletionAndImportCompletionHost() : GetImportCompletionHost(); // First completion request should kick off the task to update the completion cache. - var completions = await FindCompletionsAsync(filename, input, host, forceExpandedCompletionIndexCreation: false); + var completions = await FindCompletionsAsync(filename, input, host); Assert.True(completions.IsIncomplete); Assert.DoesNotContain("Guid", completions.Items.Select(c => c.TextEdit.NewText)); Assert.All(completions.Items, c => Assert.False(c.HasAfterInsertStep)); @@ -175,7 +175,7 @@ await Task.Run(async () => { while (completions.IsIncomplete) { - completions = await FindCompletionsAsync(filename, input, host, forceExpandedCompletionIndexCreation: false); + completions = await FindCompletionsAsync(filename, input, host); cts.Token.ThrowIfCancellationRequested(); } }, cts.Token); @@ -2253,7 +2253,7 @@ public C() private CompletionService GetCompletionService(OmniSharpTestHost host) => host.GetRequestHandler(EndpointName); - protected async Task FindCompletionsAsync(string filename, string source, OmniSharpTestHost testHost, char? triggerChar = null, TestFile[] additionalFiles = null, bool forceExpandedCompletionIndexCreation = true) + protected async Task FindCompletionsAsync(string filename, string source, OmniSharpTestHost testHost, char? triggerChar = null, TestFile[] additionalFiles = null, bool forceExpandedCompletionIndexCreation = false) { var testFile = new TestFile(filename, source); @@ -2281,29 +2281,8 @@ protected async Task FindCompletionsAsync(string filename, s return await requestHandler.Handle(request, forceExpandedCompletionIndexCreation); } - private async Task FindCompletionsWithImportedAsync(string filename, string source, OmniSharpTestHost host) - { - var completions = await FindCompletionsAsync(filename, source, host); - if (!completions.IsIncomplete) - { - return completions; - } - - // Populating the completion list should take no more than a few ms, don't let it take too - // long - CancellationTokenSource cts = new CancellationTokenSource(millisecondsDelay: ImportCompletionTimeout); - await Task.Run(async () => - { - while (completions.IsIncomplete) - { - completions = await FindCompletionsAsync(filename, source, host); - cts.Token.ThrowIfCancellationRequested(); - } - }, cts.Token); - - Assert.False(completions.IsIncomplete); - return completions; - } + private Task FindCompletionsWithImportedAsync(string filename, string source, OmniSharpTestHost host) + => FindCompletionsAsync(filename, source, host, forceExpandedCompletionIndexCreation: true); protected async Task ResolveCompletionAsync(CompletionItem completionItem, OmniSharpTestHost testHost) => await GetCompletionService(testHost).Handle(new CompletionResolveRequest { Item = completionItem }); From 8ee981016eebb71e94f4c2cd7c62b202054cf123 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 7 Mar 2022 23:40:44 -0800 Subject: [PATCH 6/6] Fix CompletionHandlerFacts tests --- .../OmnisharpCompletionHandlerFacts.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/OmniSharp.Lsp.Tests/OmnisharpCompletionHandlerFacts.cs b/tests/OmniSharp.Lsp.Tests/OmnisharpCompletionHandlerFacts.cs index 891782e933..9a5e46f78c 100644 --- a/tests/OmniSharp.Lsp.Tests/OmnisharpCompletionHandlerFacts.cs +++ b/tests/OmniSharp.Lsp.Tests/OmnisharpCompletionHandlerFacts.cs @@ -323,7 +323,7 @@ public static void Test(this object o) }"; await EnableImportCompletion(); - var completions = await FindCompletionsWithImportedAsync(filename, input); + var completions = await FindCompletionsWithImportedAsync(filename, input, items => items.Any(c => c.TextEdit.TextEdit.NewText == "Guid")); var resolved = await ResolveCompletionAsync(completions.Items.First(c => c.TextEdit.TextEdit.NewText == "Guid")); Assert.Single(resolved.AdditionalTextEdits); @@ -1559,10 +1559,10 @@ protected async Task FindCompletionsAsync(string filename, strin return await Client.RequestCompletion(request); } - private async Task FindCompletionsWithImportedAsync(string filename, string source) + private async Task FindCompletionsWithImportedAsync(string filename, string source, Func isFullyComplete = null) { var completions = await FindCompletionsAsync(filename, source); - if (!completions.IsIncomplete) + if (!completions.IsIncomplete && isFullyComplete?.Invoke(completions) == true) { return completions; } @@ -1572,7 +1572,7 @@ private async Task FindCompletionsWithImportedAsync(string filen CancellationTokenSource cts = new CancellationTokenSource(millisecondsDelay: ImportCompletionTimeout); await Task.Run(async () => { - while (completions.IsIncomplete) + while (completions.IsIncomplete || isFullyComplete?.Invoke(completions) == false) { completions = await FindCompletionsAsync(filename, source); cts.Token.ThrowIfCancellationRequested();