diff --git a/build/Packages.props b/build/Packages.props index 6c6a92a618..5b5553972e 100644 --- a/build/Packages.props +++ b/build/Packages.props @@ -7,7 +7,7 @@ 17.0.0 17.0.0 6.2.0-preview.2.80 - 4.2.0-3.22169.1 + 4.3.0-1.22215.1 2.4.1 diff --git a/src/OmniSharp.Abstractions/Configuration.cs b/src/OmniSharp.Abstractions/Configuration.cs index 9d53c75e30..6d5484c10c 100644 --- a/src/OmniSharp.Abstractions/Configuration.cs +++ b/src/OmniSharp.Abstractions/Configuration.cs @@ -4,7 +4,7 @@ internal static class Configuration { public static bool ZeroBasedIndices = false; - public const string RoslynVersion = "4.2.0.0"; + public const string RoslynVersion = "4.3.0.0"; public const string RoslynPublicKeyToken = "31bf3856ad364e35"; public readonly static string RoslynFeatures = GetRoslynAssemblyFullName("Microsoft.CodeAnalysis.Features"); diff --git a/src/OmniSharp.Abstractions/Models/v1/InlayHints/InlayHint.cs b/src/OmniSharp.Abstractions/Models/v1/InlayHints/InlayHint.cs index 49a18b8a21..22a6dfcf7c 100644 --- a/src/OmniSharp.Abstractions/Models/v1/InlayHints/InlayHint.cs +++ b/src/OmniSharp.Abstractions/Models/v1/InlayHints/InlayHint.cs @@ -6,15 +6,36 @@ namespace OmniSharp.Models.v1.InlayHints; public sealed record InlayHint { + /// + /// The position of this hint. + /// public Point Position { get; set; } + + /// + /// The label of this hint. A human readable string. + /// public string Label { get; set; } + + /// + /// The tooltip text when you hover over this item. + /// public string? Tooltip { get; set; } + + /// + /// Optional text edits that are performed when accepting this inlay hint. + /// + public LinePositionSpanTextChange[]? TextEdits { get; set; } + + /// + /// A data entry field that is preserved on a inlay hint between a and a . + /// public (string SolutionVersion, int Position) Data { get; set; } #nullable enable public override string ToString() { - return $"InlineHint {{ {nameof(Position)} = {Position}, {nameof(Label)} = {Label}, {nameof(Tooltip)} = {Tooltip} }}"; + var textEdits = TextEdits is null ? "null" : $"[ {string.Join(", ", TextEdits)} ]"; + return $"InlayHint {{ {nameof(Position)} = {Position}, {nameof(Label)} = {Label}, {nameof(Tooltip)} = {Tooltip ?? "null"}, {nameof(TextEdits)} = {textEdits} }}"; } public bool Equals(InlayHint? other) @@ -22,14 +43,51 @@ public bool Equals(InlayHint? other) if (ReferenceEquals(this, other)) return true; if (other is null) return false; - return Position == other.Position && Label == other.Label && Tooltip == other.Tooltip; + return Position == other.Position + && Label == other.Label + && Tooltip == other.Tooltip + && TextEditsEqual(TextEdits, other.TextEdits); + } + + private static bool TextEditsEqual(LinePositionSpanTextChange[]? a, LinePositionSpanTextChange[]? b) + { + if (a is null) + { + return b is null; + } + + if (b is null) + { + return false; + } + + if (a.Length != b.Length) + { + return false; + } + + for (int index = 0; index < a.Length; index++) + { + if (!a[index].Equals(b[index])) + { + return false; + } + } + + return true; } - public override int GetHashCode() => (Position, Label, Tooltip).GetHashCode(); + public override int GetHashCode() => (Position, Label, Tooltip, TextEdits?.GetHashCode() ?? 0).GetHashCode(); } public enum InlayHintKind { + /// + /// An inlay hint that is for a type annotation. + /// Type = 1, + /// + /// An inlay hint that is for a parameter. + /// Parameter = 2, } diff --git a/src/OmniSharp.Abstractions/Models/v1/LinePositionSpanTextChange.cs b/src/OmniSharp.Abstractions/Models/v1/LinePositionSpanTextChange.cs index 3a7363fb6a..5e3a728b26 100644 --- a/src/OmniSharp.Abstractions/Models/v1/LinePositionSpanTextChange.cs +++ b/src/OmniSharp.Abstractions/Models/v1/LinePositionSpanTextChange.cs @@ -43,9 +43,9 @@ public override string ToString() { var displayText = NewText != null ? NewText.Replace("\r", @"\r").Replace("\n", @"\n").Replace("\t", @"\t") - : string.Empty; + : "null"; - return $"StartLine={StartLine}, StartColumn={StartColumn}, EndLine={EndLine}, EndColumn={EndColumn}, NewText='{displayText}'"; + return $"LinePositionSpanTextChange {{ StartLine={StartLine}, StartColumn={StartColumn}, EndLine={EndLine}, EndColumn={EndColumn}, NewText={displayText} }}"; } } } diff --git a/src/OmniSharp.Cake/Services/RequestHandlers/Completion/CompletionHandler.cs b/src/OmniSharp.Cake/Services/RequestHandlers/Completion/CompletionHandler.cs index 5de5fe1878..addf4c8553 100644 --- a/src/OmniSharp.Cake/Services/RequestHandlers/Completion/CompletionHandler.cs +++ b/src/OmniSharp.Cake/Services/RequestHandlers/Completion/CompletionHandler.cs @@ -18,6 +18,17 @@ public CompletionHandler(OmniSharpWorkspace workspace) : base(workspace) protected override Task TranslateResponse(CompletionResponse response, CompletionRequest request) { + if (response.Items is { Count: > 0 }) + { + // In some instances, formatting changes to generated Cake DSL are being + // included in the CompletionItem as AdditionalTextEdits. The short term + // fix is to remove AdditionalTextEdits for now. + foreach (var item in response.Items) + { + item.AdditionalTextEdits = null; + } + } + return response.TranslateAsync(Workspace, request); } } diff --git a/src/OmniSharp.Http.Driver/app.config b/src/OmniSharp.Http.Driver/app.config index c69ce59989..24ab3d4192 100644 --- a/src/OmniSharp.Http.Driver/app.config +++ b/src/OmniSharp.Http.Driver/app.config @@ -7,23 +7,23 @@ - + - + - + - + - + diff --git a/src/OmniSharp.LanguageServerProtocol/app.config b/src/OmniSharp.LanguageServerProtocol/app.config index 0f2ad45129..991918fe41 100644 --- a/src/OmniSharp.LanguageServerProtocol/app.config +++ b/src/OmniSharp.LanguageServerProtocol/app.config @@ -7,23 +7,23 @@ - + - + - + - + - + diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionListBuilder_Sync.cs b/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionListBuilder_Sync.cs index b1a0ea746b..087f7e3870 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionListBuilder_Sync.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionListBuilder_Sync.cs @@ -4,9 +4,11 @@ using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Completion; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Text; +using OmniSharp.Extensions; using OmniSharp.Models; using OmniSharp.Models.v1.Completion; using OmniSharp.Roslyn.CSharp.Helpers; +using OmniSharp.Roslyn.Utilities; using OmniSharp.Utilities; using System.Collections.Generic; using System.Collections.Immutable; @@ -17,7 +19,6 @@ using CSharpCompletionItem = Microsoft.CodeAnalysis.Completion.CompletionItem; using CSharpCompletionList = Microsoft.CodeAnalysis.Completion.CompletionList; using CSharpCompletionService = Microsoft.CodeAnalysis.Completion.CompletionService; -using OmniSharp.Extensions; namespace OmniSharp.Roslyn.CSharp.Services.Completion { @@ -270,7 +271,7 @@ private static void GetCompletionInfo( static void handleNonInsertsectingEdit(SourceText sourceText, ref List? additionalTextEdits, ref int? adjustedNewPosition, TextChange textChange) { additionalTextEdits ??= new(); - additionalTextEdits.Add(GetChangeForTextAndSpan(textChange.NewText!, textChange.Span, sourceText)); + additionalTextEdits.Add(TextChanges.Convert(sourceText, textChange)); if (adjustedNewPosition is int newPosition) { diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs index fab7d0fb5a..5b685685de 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionService.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; @@ -19,11 +18,12 @@ using OmniSharp.Models.v1.Completion; using OmniSharp.Options; using OmniSharp.Roslyn.CSharp.Helpers; +using OmniSharp.Roslyn.Utilities; using OmniSharp.Utilities; +using Roslyn.Utilities; using CompletionItem = OmniSharp.Models.v1.Completion.CompletionItem; using CompletionTriggerKind = OmniSharp.Models.v1.Completion.CompletionTriggerKind; using CSharpCompletionService = Microsoft.CodeAnalysis.Completion.CompletionService; -using Roslyn.Utilities; namespace OmniSharp.Roslyn.CSharp.Services.Completion { @@ -207,7 +207,7 @@ public async Task Handle(CompletionResolveRequest req continue; } - additionalChanges.Add(CompletionListBuilder.GetChangeForTextAndSpan(textChange.NewText, textChange.Span, sourceText)); + additionalChanges.Add(TextChanges.Convert(sourceText, textChange)); } request.Item.AdditionalTextEdits = additionalChanges; @@ -273,7 +273,7 @@ public async Task Handle(CompletionAfterInsertReq return new CompletionAfterInsertResponse { - Changes = finalChange.TextChanges.SelectAsArray(changedText, static (c, changedText) => CompletionListBuilder.GetChangeForTextAndSpan(c.NewText, c.Span, changedText)), + Changes = finalChange.TextChanges.SelectAsArray(changedText, static (c, changedText) => TextChanges.Convert(changedText, c)), Line = finalPosition.Line, Column = finalPosition.Column, }; diff --git a/src/OmniSharp.Roslyn.CSharp/Services/InlayHints/InlayHintService.cs b/src/OmniSharp.Roslyn.CSharp/Services/InlayHints/InlayHintService.cs index 19ab42e577..b741a37848 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/InlayHints/InlayHintService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/InlayHints/InlayHintService.cs @@ -12,9 +12,11 @@ using Microsoft.Extensions.Options; using OmniSharp.Extensions; using OmniSharp.Mef; +using OmniSharp.Models; using OmniSharp.Models.v1.InlayHints; using OmniSharp.Options; using OmniSharp.Roslyn.CSharp.Helpers; +using OmniSharp.Roslyn.Utilities; #nullable enable @@ -139,6 +141,7 @@ public List MapAndCacheHints(ImmutableArray rosl { Label = string.Concat(hint.DisplayParts), Position = text.GetPointFromPosition(hint.Span.End), + TextEdits = ConvertToTextChanges(hint.ReplacementTextChange, text), Data = (solutionVersionString, position) }); @@ -152,6 +155,13 @@ public List MapAndCacheHints(ImmutableArray rosl return resultList; } + internal static LinePositionSpanTextChange[]? ConvertToTextChanges(TextChange? textChange, SourceText sourceText) + { + return textChange.HasValue + ? new[] { TextChanges.Convert(sourceText, textChange.Value) } + : null; + } + public bool TryGetFromCache(InlayHint hint, out OmniSharpInlineHint roslynHint, [NotNullWhen(true)] out Document? document) { (roslynHint, document) = (default, null); diff --git a/src/OmniSharp.Roslyn/MetadataExternalSourceService.cs b/src/OmniSharp.Roslyn.CSharp/Services/MetadataExternalSourceService.cs similarity index 83% rename from src/OmniSharp.Roslyn/MetadataExternalSourceService.cs rename to src/OmniSharp.Roslyn.CSharp/Services/MetadataExternalSourceService.cs index 726d03312b..c18d69494f 100644 --- a/src/OmniSharp.Roslyn/MetadataExternalSourceService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/MetadataExternalSourceService.cs @@ -5,6 +5,8 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.MetadataAsSource; using OmniSharp.Extensions; +using OmniSharp.Options; +using OmniSharp.Roslyn.CSharp.Workers.Formatting; namespace OmniSharp.Roslyn { @@ -13,9 +15,12 @@ public class MetadataExternalSourceService : BaseExternalSourceService, IExterna { private const string MetadataKey = "$Metadata$"; + private readonly OmniSharpOptions _omnisharpOptions; + [ImportingConstructor] - public MetadataExternalSourceService() : base() + public MetadataExternalSourceService(OmniSharpOptions omnisharpOptions) : base() { + _omnisharpOptions = omnisharpOptions; } public async Task<(Document document, string documentPath)> GetAndAddExternalSymbolDocument(Project project, ISymbol symbol, CancellationToken cancellationToken) @@ -47,11 +52,13 @@ public MetadataExternalSourceService() : base() var topLevelSymbol = symbol.GetTopLevelContainingNamedType(); var temporaryDocument = metadataProject.AddDocument(fileName, string.Empty); + var formattingOptions = await FormattingWorker.GetFormattingOptionsAsync(temporaryDocument, _omnisharpOptions); document = await OmniSharpMetadataAsSourceService.AddSourceToAsync( temporaryDocument, await metadataProject.GetCompilationAsync(), topLevelSymbol, + formattingOptions, cancellationToken); _cache.TryAdd(fileName, document); diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RunFixAllCodeActionService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RunFixAllCodeActionService.cs index 4a54e5a1ef..f04b9a8db9 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RunFixAllCodeActionService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Refactoring/RunFixAllCodeActionService.cs @@ -168,8 +168,16 @@ private async Task FixSpecificDiagnosticIdAsync(Document document, str }; var fixAllContext = OmniSharpCodeFixContextFactory.CreateFixAllContext( - document, document.Project, codeFixProvider, roslynScope, action.EquivalenceKey, ImmutableArray.Create(diagnosticId), _fixAllDiagnosticProvider, - _ => codeActionOptions, cancellationToken); + document, + primaryDiagnostic.Location.SourceSpan, + 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/Workers/Diagnostics/CSharpDiagnosticWorkerWithAnalyzers.cs b/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticWorkerWithAnalyzers.cs index 62590c5dee..1c9ddd91f2 100644 --- a/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticWorkerWithAnalyzers.cs +++ b/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticWorkerWithAnalyzers.cs @@ -33,6 +33,8 @@ public class CSharpDiagnosticWorkerWithAnalyzers : ICsDiagnosticWorker, IDisposa private readonly OmniSharpOptions _options; private readonly OmniSharpWorkspace _workspace; + private const int WorkerWait = 250; + public CSharpDiagnosticWorkerWithAnalyzers( OmniSharpWorkspace workspace, [ImportMany] IEnumerable providers, @@ -114,6 +116,15 @@ private async Task Worker(AnalyzerWorkType workType) .Where(x => x.projectId != null) .ToImmutableArray(); + if (documents.IsEmpty) + { + _workQueue.WorkComplete(workType); + + await Task.Delay(WorkerWait); + + continue; + } + var documentCount = documents.Length; var documentCountRemaining = documentCount; @@ -157,7 +168,7 @@ void decrementDocumentCountRemaining() _workQueue.WorkComplete(workType); - await Task.Delay(50); + await Task.Delay(WorkerWait); } catch (Exception ex) { @@ -219,7 +230,7 @@ public async Task> AnalyzeDocumentAsync(Document documen Project project = document.Project; var allAnalyzers = GetAnalyzersForProject(project); var compilation = await project.GetCompilationAsync(cancellationToken); - + cancellationToken.ThrowIfCancellationRequested(); return await AnalyzeDocument(project, allAnalyzers, compilation, CreateAnalyzerOptions(document.Project), document); } diff --git a/src/OmniSharp.Roslyn/Utilities/TextChangeHelper.cs b/src/OmniSharp.Roslyn/Utilities/TextChangeHelper.cs index dbac3714d5..afb668f908 100644 --- a/src/OmniSharp.Roslyn/Utilities/TextChangeHelper.cs +++ b/src/OmniSharp.Roslyn/Utilities/TextChangeHelper.cs @@ -17,57 +17,54 @@ public static async Task> GetAsync(Docum return Convert(oldText, changes); } - public static IEnumerable Convert(SourceText oldText, params TextChange[] changes) + public static LinePositionSpanTextChange Convert(SourceText oldText, TextChange change) { - return Convert(oldText, (IEnumerable)changes); - } + var span = change.Span; + var newText = change.NewText; + var prefix = string.Empty; + var postfix = string.Empty; - public static IEnumerable Convert(SourceText oldText, IEnumerable changes) - { - return changes - .OrderByDescending(change => change.Span) - .Select(change => - { - var span = change.Span; - var newText = change.NewText; - var prefix = string.Empty; - var postfix = string.Empty; + if (newText.Length > 0) + { + // Roslyn computes text changes on character arrays. So it might happen that a + // change starts inbetween \r\n which is OK when you are offset-based but a problem + // when you are line,column-based. This code extends text edits which just overlap + // a with a line break to its full line break - if (newText.Length > 0) - { - // Roslyn computes text changes on character arrays. So it might happen that a - // change starts inbetween \r\n which is OK when you are offset-based but a problem - // when you are line,column-based. This code extends text edits which just overlap - // a with a line break to its full line break + if (span.Start > 0 && newText[0] == '\n' && oldText[span.Start - 1] == '\r') + { + // text: foo\r\nbar\r\nfoo + // edit: [----) + span = TextSpan.FromBounds(span.Start - 1, span.End); + prefix = "\r"; + } - if (span.Start > 0 && newText[0] == '\n' && oldText[span.Start - 1] == '\r') - { - // text: foo\r\nbar\r\nfoo - // edit: [----) - span = TextSpan.FromBounds(span.Start - 1, span.End); - prefix = "\r"; - } + if (span.End < oldText.Length - 1 && newText[newText.Length - 1] == '\r' && oldText[span.End] == '\n') + { + // text: foo\r\nbar\r\nfoo + // edit: [----) + span = TextSpan.FromBounds(span.Start, span.End + 1); + postfix = "\n"; + } + } - if (span.End < oldText.Length - 1 && newText[newText.Length - 1] == '\r' && oldText[span.End] == '\n') - { - // text: foo\r\nbar\r\nfoo - // edit: [----) - span = TextSpan.FromBounds(span.Start, span.End + 1); - postfix = "\n"; - } - } + var linePositionSpan = oldText.Lines.GetLinePositionSpan(span); - var linePositionSpan = oldText.Lines.GetLinePositionSpan(span); + return new LinePositionSpanTextChange() + { + NewText = prefix + newText + postfix, + StartLine = linePositionSpan.Start.Line, + StartColumn = linePositionSpan.Start.Character, + EndLine = linePositionSpan.End.Line, + EndColumn = linePositionSpan.End.Character + }; + } - return new LinePositionSpanTextChange() - { - NewText = prefix + newText + postfix, - StartLine = linePositionSpan.Start.Line, - StartColumn = linePositionSpan.Start.Character, - EndLine = linePositionSpan.End.Line, - EndColumn = linePositionSpan.End.Character - }; - }); + public static IEnumerable Convert(SourceText oldText, IEnumerable changes) + { + return changes + .OrderByDescending(change => change.Span) + .Select(change => Convert(oldText, change)); } } } diff --git a/src/OmniSharp.Stdio.Driver/app.config b/src/OmniSharp.Stdio.Driver/app.config index c69ce59989..24ab3d4192 100644 --- a/src/OmniSharp.Stdio.Driver/app.config +++ b/src/OmniSharp.Stdio.Driver/app.config @@ -7,23 +7,23 @@ - + - + - + - + - + diff --git a/tests/OmniSharp.Cake.Tests/CompletionFacts.cs b/tests/OmniSharp.Cake.Tests/CompletionFacts.cs index 874f07ec66..2a07ed3fca 100644 --- a/tests/OmniSharp.Cake.Tests/CompletionFacts.cs +++ b/tests/OmniSharp.Cake.Tests/CompletionFacts.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using OmniSharp.Cake.Services.RequestHandlers.Completion; @@ -15,7 +14,7 @@ namespace OmniSharp.Cake.Tests { public class CompletionFacts : CakeSingleRequestHandlerTestFixture { - private const int ImportCompletionTimeout = 1000; + private const int ImportCompletionTimeout = 2000; private readonly ILogger _logger; public CompletionFacts(ITestOutputHelper testOutput) : base(testOutput) @@ -30,7 +29,7 @@ public async Task ShouldGetCompletionFromHostObject() { const string input = @"TaskSe$$"; - using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy : false)) + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy: false)) using (var host = CreateOmniSharpHost(testProject.Directory)) { var fileName = Path.Combine(testProject.Directory, "build.cake"); @@ -50,7 +49,7 @@ public async Task ShouldGetCompletionFromDSL() Inform$$ });"; - using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy : false)) + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy: false)) using (var host = CreateOmniSharpHost(testProject.Directory)) { var fileName = Path.Combine(testProject.Directory, "build.cake"); @@ -70,7 +69,7 @@ public async Task ShouldResolveFromDSL() Inform$$ });"; - using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy : false)) + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy: false)) using (var host = CreateOmniSharpHost(testProject.Directory)) { var fileName = Path.Combine(testProject.Directory, "build.cake"); @@ -90,7 +89,7 @@ public async Task ShouldRemoveAdditionalTextEditsFromResolvedCompletions() { const string input = @"var regex = new Rege$$"; - using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy : false)) + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy: false)) using (var host = CreateOmniSharpHost(testProject.Directory, new[] { new KeyValuePair("RoslynExtensionsOptions:EnableImportCompletion", "true") })) { @@ -103,16 +102,9 @@ public async Task ShouldRemoveAdditionalTextEditsFromResolvedCompletions() // Populating the completion cache should take no more than a few ms, don't let it take too // long - var cts = new CancellationTokenSource(millisecondsDelay: ImportCompletionTimeout); - await Task.Run(async () => - { - while (completions.IsIncomplete) - { - completions = await FindCompletionsAsync(fileName, input, host); - cts.Token.ThrowIfCancellationRequested(); - } - }, cts.Token); + await Task.Delay(ImportCompletionTimeout); + completions = await FindCompletionsAsync(fileName, input, host); Assert.False(completions.IsIncomplete); Assert.Contains("Regex", completions.Items.Select(c => c.TextEdit.NewText)); @@ -126,7 +118,7 @@ await Task.Run(async () => } [Fact] - public async Task ShouldGetAdditionalTextEditsFromOverrideCompletion() + public async Task ShouldNotGetAdditionalTextEditsFromOverrideCompletion() { const string source = @" class Foo @@ -141,7 +133,7 @@ class FooChild : Foo } "; - using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy : false)) + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy: false)) using (var host = CreateOmniSharpHost(testProject.Directory)) { var fileName = Path.Combine(testProject.Directory, "build.cake"); @@ -149,8 +141,11 @@ class FooChild : Foo Assert.Equal( new[] { - "Equals(object? obj)", "GetHashCode()", "Test(string text)", - "Test(string text, string moreText)", "ToString()" + "Equals(object? obj)", + "GetHashCode()", + "Test(string text)", + "Test(string text, string moreText)", + "ToString()" }, completions.Items.Select(c => c.Label)); Assert.Equal(new[] diff --git a/tests/OmniSharp.Lsp.Tests/OmnisharpCompletionHandlerFacts.cs b/tests/OmniSharp.Lsp.Tests/OmnisharpCompletionHandlerFacts.cs index 9a5e46f78c..7d8f0f810d 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, items => items.Any(c => c.TextEdit.TextEdit.NewText == "Guid")); + var completions = await FindCompletionsWithImportedAsync(filename, input); var resolved = await ResolveCompletionAsync(completions.Items.First(c => c.TextEdit.TextEdit.NewText == "Guid")); Assert.Single(resolved.AdditionalTextEdits); @@ -1559,25 +1559,19 @@ protected async Task FindCompletionsAsync(string filename, strin return await Client.RequestCompletion(request); } - private async Task FindCompletionsWithImportedAsync(string filename, string source, Func isFullyComplete = null) + private async Task FindCompletionsWithImportedAsync(string filename, string source) { var completions = await FindCompletionsAsync(filename, source); - if (!completions.IsIncomplete && isFullyComplete?.Invoke(completions) == true) + 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 || isFullyComplete?.Invoke(completions) == false) - { - completions = await FindCompletionsAsync(filename, source); - cts.Token.ThrowIfCancellationRequested(); - } - }, cts.Token); + await Task.Delay(ImportCompletionTimeout); + + completions = await FindCompletionsAsync(filename, source); Assert.False(completions.IsIncomplete); return completions; diff --git a/tests/OmniSharp.MSBuild.Tests/ProjectWithAnalyzersTests.cs b/tests/OmniSharp.MSBuild.Tests/ProjectWithAnalyzersTests.cs index 1b3e0da07b..fbe0026754 100644 --- a/tests/OmniSharp.MSBuild.Tests/ProjectWithAnalyzersTests.cs +++ b/tests/OmniSharp.MSBuild.Tests/ProjectWithAnalyzersTests.cs @@ -286,7 +286,7 @@ public async Task WhenNewAnalyzerReferenceIsAdded_ThenAutomaticallyUseItWithoutR csprojFileXml => { var referencesGroup = csprojFileXml.Descendants("ItemGroup").FirstOrDefault(); - referencesGroup.Add(new XElement("PackageReference", new XAttribute("Include", "Roslynator.Analyzers"), new XAttribute("Version", "2.1.0"))); + referencesGroup.Add(new XElement("PackageReference", new XAttribute("Include", "Roslynator.Analyzers"), new XAttribute("Version", "4.1.0"), new XAttribute("PrivateAssets", "all"), new XAttribute("IncludeAssets", "runtime; build; native; contentfiles; analyzers"))); }); await NotifyFileChanged(host, csprojFile); @@ -295,7 +295,7 @@ public async Task WhenNewAnalyzerReferenceIsAdded_ThenAutomaticallyUseItWithoutR await host.RestoreProject(testProject); // Todo: This can be removed and replaced with wait for event (project analyzed eg.) once they are available. - await Task.Delay(2000); + await Task.Delay(5000); var diagnostics = await host.RequestCodeCheckAsync(Path.Combine(testProject.Directory, "Program.cs")); diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs index 69ffd79ba8..9d2fc512b9 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/CompletionFacts.cs @@ -17,7 +17,7 @@ namespace OmniSharp.Roslyn.CSharp.Tests { public class CompletionFacts : AbstractTestFixture { - private const int ImportCompletionTimeout = 1000; + private const int ImportCompletionTimeout = 2000; private readonly ILogger _logger; private string EndpointName => OmniSharpEndpoints.Completion; diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/EditorConfigFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/EditorConfigFacts.cs index 2d9d0d632e..5cd62c9dfc 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/EditorConfigFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/EditorConfigFacts.cs @@ -186,7 +186,7 @@ class Bar:Foo { } Line = point.Line, Column = point.Offset, FileName = testFile.FileName, - Identifier = "Fix formatting", + Identifier = "AbstractFormattingCodeFixProvider", WantsTextChanges = false, WantsAllCodeActionOperations = true, Buffer = testFile.Content.Code diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/InlayHintsFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/InlayHintsFacts.cs index 3902c3c227..04754a3438 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/InlayHintsFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/InlayHintsFacts.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using OmniSharp.Models; using OmniSharp.Models.v1.InlayHints; using OmniSharp.Models.V2; using OmniSharp.Options; @@ -36,10 +37,10 @@ class C { } var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 3, Column = 2 }, Label = "param1: ", Tooltip = null }, - new InlayHint { Position = new Point { Line = 3, Column = 9 }, Label = "paramB: ", Tooltip = null }, - new InlayHint { Position = new Point { Line = 1, Column = 4 }, Label = "C ", Tooltip = null }, - new InlayHint { Position = new Point { Line = 2, Column = 4 }, Label = "C ", Tooltip = null } + new InlayHint { Position = new Point { Line = 3, Column = 2 }, Label = "param1: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 3, StartColumn = 2, EndLine = 3, EndColumn = 2, NewText = "param1: " } } }, + new InlayHint { Position = new Point { Line = 3, Column = 9 }, Label = "paramB: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 3, StartColumn = 9, EndLine = 3, EndColumn = 9, NewText = "paramB: " } } }, + new InlayHint { Position = new Point { Line = 1, Column = 4 }, Label = "C ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 1, StartColumn = 0, EndLine = 1, EndColumn = 3, NewText = "C" } } }, + new InlayHint { Position = new Point { Line = 2, Column = 4 }, Label = "C ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 2, StartColumn = 0, EndLine = 2, EndColumn = 3, NewText = "C" } } }, }, response.InlayHints); @@ -87,8 +88,8 @@ void M(int param1, int paramB) { } var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 1, Column = 4 }, Label = "int ", Tooltip = null }, - new InlayHint { Position = new Point { Line = 2, Column = 4 }, Label = "int ", Tooltip = null } + new InlayHint { Position = new Point { Line = 1, Column = 4 }, Label = "int ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 1, StartColumn = 0, EndLine = 1, EndColumn = 3, NewText = "int" } } }, + new InlayHint { Position = new Point { Line = 2, Column = 4 }, Label = "int ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 2, StartColumn = 0, EndLine = 2, EndColumn = 3, NewText = "int" } } }, }, response.InlayHints); } @@ -112,8 +113,8 @@ void M(int param1, int paramB) { } var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 3, Column = 2 }, Label = "param1: ", Tooltip = null }, - new InlayHint { Position = new Point { Line = 3, Column = 9 }, Label = "paramB: ", Tooltip = null }, + new InlayHint { Position = new Point { Line = 3, Column = 2 }, Label = "param1: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 3, StartColumn = 2, EndLine = 3, EndColumn = 2, NewText = "param1: " } } }, + new InlayHint { Position = new Point { Line = 3, Column = 9 }, Label = "paramB: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 3, StartColumn = 9, EndLine = 3, EndColumn = 9, NewText = "paramB: " } } }, }, response.InlayHints); } @@ -141,7 +142,7 @@ public async Task InlayHintsForVarTypes(string fileName) var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 1, Column = 4 }, Label = "int ", Tooltip = null } + new InlayHint { Position = new Point { Line = 1, Column = 4 }, Label = "int ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 1, StartColumn = 0, EndLine = 1, EndColumn = 3, NewText = "int" } } }, }, response.InlayHints); } @@ -170,8 +171,8 @@ public async Task InlayHintsForLambdaParameterTypes(string fileName) var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 2, Column = 34 }, Label = "int ", Tooltip = null }, - new InlayHint { Position = new Point { Line = 2, Column = 37 }, Label = "string ", Tooltip = null } + new InlayHint { Position = new Point { Line = 2, Column = 34 }, Label = "int ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 2, StartColumn = 34, EndLine = 2, EndColumn = 34, NewText = "int " } } }, + new InlayHint { Position = new Point { Line = 2, Column = 37 }, Label = "string ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 2, StartColumn = 37, EndLine = 2, EndColumn = 37, NewText = "string " } } } }, response.InlayHints); } @@ -199,7 +200,7 @@ public async Task InlayHintsForImplicitObjectCreation(string fileName) var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 1, Column = 14 }, Label = " string", Tooltip = null } + new InlayHint { Position = new Point { Line = 1, Column = 14 }, Label = " string", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 1, StartColumn = 14, EndLine = 1, EndColumn = 14, NewText = " string" } } } }, response.InlayHints); } @@ -228,7 +229,7 @@ void M(int i) {} var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 1, Column = 2 }, Label = "i: ", Tooltip = null } + new InlayHint { Position = new Point { Line = 1, Column = 2 }, Label = "i: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 1, StartColumn = 2, EndLine = 1, EndColumn = 2, NewText = "i: " } } } }, response.InlayHints); } @@ -263,8 +264,8 @@ class C var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 3, Column = 2 }, Label = "test: ", Tooltip = null }, - new InlayHint { Position = new Point { Line = 3, Column = 9 }, Label = "test: ", Tooltip = null } + new InlayHint { Position = new Point { Line = 3, Column = 2 }, Label = "test: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 3, StartColumn = 2, EndLine = 3, EndColumn = 2, NewText = "test: " } } }, + new InlayHint { Position = new Point { Line = 3, Column = 9 }, Label = "test: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 3, StartColumn = 9, EndLine = 3, EndColumn = 9, NewText = "test: " } } } }, response.InlayHints); } @@ -299,7 +300,7 @@ class C var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 2, Column = 2 }, Label = "c: ", Tooltip = null } + new InlayHint { Position = new Point { Line = 2, Column = 2 }, Label = "c: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 2, StartColumn = 2, EndLine = 2, EndColumn = 2, NewText = "c: " } } } }, response.InlayHints); } @@ -332,7 +333,7 @@ void M(int test) {} var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 2, Column = 2 }, Label = "test: ", Tooltip = null } + new InlayHint { Position = new Point { Line = 2, Column = 2 }, Label = "test: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 2, StartColumn = 2, EndLine = 2, EndColumn = 2, NewText = "test: " } } } }, response.InlayHints); } @@ -363,8 +364,8 @@ void M(int test1, int test2) {} var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 1, Column = 2 }, Label = "test1: ", Tooltip = null }, - new InlayHint { Position = new Point { Line = 1, Column = 5 }, Label = "test2: ", Tooltip = null } + new InlayHint { Position = new Point { Line = 1, Column = 2 }, Label = "test1: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 1, StartColumn = 2, EndLine = 1, EndColumn = 2, NewText = "test1: " } } }, + new InlayHint { Position = new Point { Line = 1, Column = 5 }, Label = "test2: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 1, StartColumn = 5, EndLine = 1, EndColumn = 5, NewText = "test2: " } } } }, response.InlayHints); } @@ -397,8 +398,8 @@ public static void EnableSomething(bool enabled) {} var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 1, Column = 18 }, Label = "enabled: ", Tooltip = null } - }, + new InlayHint { Position = new Point { Line = 1, Column = 18 }, Label = "enabled: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 1, StartColumn = 18, EndLine = 1, EndColumn = 18, NewText = "enabled: " } } } + }, response.InlayHints); } } @@ -432,7 +433,7 @@ public static void M(int i) {} var response = await GetInlayHints(fileName, code, testHost); AssertEx.Equal(new[] { - new InlayHint { Position = new Point { Line = 2, Column = 4 }, Label = "i: ", Tooltip = null } + new InlayHint { Position = new Point { Line = 2, Column = 4 }, Label = "i: ", Tooltip = null, TextEdits = new[] { new LinePositionSpanTextChange { StartLine = 2, StartColumn = 4, EndLine = 2, EndColumn = 4, NewText = "i: " } } } }, response.InlayHints); } diff --git a/tests/OmniSharp.Tests/LinePositionSpanTextChangeFacts.cs b/tests/OmniSharp.Tests/LinePositionSpanTextChangeFacts.cs index b2d70b2bc5..d2847e5468 100644 --- a/tests/OmniSharp.Tests/LinePositionSpanTextChangeFacts.cs +++ b/tests/OmniSharp.Tests/LinePositionSpanTextChangeFacts.cs @@ -26,9 +26,8 @@ public async Task ExtendsTextChangeAtStart() var textChange = new TextChange(TextSpan.FromBounds(8, 11), "\n}"); - var adjustedTextChanges = TextChanges.Convert(text, textChange); + var adjustedTextChange = TextChanges.Convert(text, textChange); - var adjustedTextChange = adjustedTextChanges.First(); Assert.Equal("\r\n}", adjustedTextChange.NewText); Assert.Equal(0, adjustedTextChange.StartLine); Assert.Equal(7, adjustedTextChange.StartColumn); @@ -48,9 +47,8 @@ public async Task ExtendsTextChangeAtEnd() var textChange = new TextChange(TextSpan.FromBounds(5, 7), "\r\n {\r"); - var adjustedTextChanges = TextChanges.Convert(text, textChange); + var adjustedTextChange = TextChanges.Convert(text, textChange); - var adjustedTextChange = adjustedTextChanges.First(); Assert.Equal("\r\n {\r\n", adjustedTextChange.NewText); Assert.Equal(0, adjustedTextChange.StartLine); Assert.Equal(5, adjustedTextChange.StartColumn); diff --git a/tests/app.config b/tests/app.config index 4927f89978..71f57a112e 100644 --- a/tests/app.config +++ b/tests/app.config @@ -7,23 +7,23 @@ - + - + - + - + - +