Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TextEdits support to InlayHints #2385

Merged
merged 10 commits into from
Apr 18, 2022
2 changes: 1 addition & 1 deletion build/Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<MicrosoftTestPackageVersion>17.0.0</MicrosoftTestPackageVersion>
<MSBuildPackageVersion>17.0.0</MSBuildPackageVersion>
<NuGetPackageVersion>6.2.0-preview.2.80</NuGetPackageVersion>
<RoslynPackageVersion>4.2.0-3.22169.1</RoslynPackageVersion>
<RoslynPackageVersion>4.3.0-1.22215.1</RoslynPackageVersion>
<XunitPackageVersion>2.4.1</XunitPackageVersion>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion src/OmniSharp.Abstractions/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
64 changes: 61 additions & 3 deletions src/OmniSharp.Abstractions/Models/v1/InlayHints/InlayHint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,88 @@ namespace OmniSharp.Models.v1.InlayHints;

public sealed record InlayHint
{
/// <summary>
/// The position of this hint.
/// </summary>
public Point Position { get; set; }

/// <summary>
/// The label of this hint. A human readable string.
/// </summary>
public string Label { get; set; }

/// <summary>
/// The tooltip text when you hover over this item.
/// </summary>
public string? Tooltip { get; set; }

/// <summary>
/// Optional text edits that are performed when accepting this inlay hint.
/// </summary>
public LinePositionSpanTextChange[]? TextEdits { get; set; }

/// <summary>
/// A data entry field that is preserved on a inlay hint between a <see cref="InlayHintRequest" /> and a <see cref="InlayHintResolveRequest" />.
/// </summary>
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<LinePositionSpanTextChange>(", ", TextEdits)} ]";
return $"InlayHint {{ {nameof(Position)} = {Position}, {nameof(Label)} = {Label}, {nameof(Tooltip)} = {Tooltip ?? "null"}, {nameof(TextEdits)} = {textEdits} }}";
}

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
{
/// <summary>
/// An inlay hint that is for a type annotation.
/// </summary>
Type = 1,
/// <summary>
/// An inlay hint that is for a parameter.
/// </summary>
Parameter = 2,
}
Original file line number Diff line number Diff line change
Expand Up @@ -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} }}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ public CompletionHandler(OmniSharpWorkspace workspace) : base(workspace)

protected override Task<CompletionResponse> 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);
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/OmniSharp.Http.Driver/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.Workspaces" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.Features" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Features" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Dataflow" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
Expand Down
10 changes: 5 additions & 5 deletions src/OmniSharp.LanguageServerProtocol/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.Workspaces" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.Features" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Features" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Dataflow" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
{
Expand Down Expand Up @@ -270,7 +271,7 @@ internal static partial class CompletionListBuilder
static void handleNonInsertsectingEdit(SourceText sourceText, ref List<LinePositionSpanTextChange>? 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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
{
Expand Down Expand Up @@ -207,7 +207,7 @@ public async Task<CompletionResolveResponse> Handle(CompletionResolveRequest req
continue;
}

additionalChanges.Add(CompletionListBuilder.GetChangeForTextAndSpan(textChange.NewText, textChange.Span, sourceText));
additionalChanges.Add(TextChanges.Convert(sourceText, textChange));
}

request.Item.AdditionalTextEdits = additionalChanges;
Expand Down Expand Up @@ -273,7 +273,7 @@ public async Task<CompletionAfterInsertResponse> 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,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -139,6 +141,7 @@ public List<InlayHint> MapAndCacheHints(ImmutableArray<OmniSharpInlineHint> rosl
{
Label = string.Concat(hint.DisplayParts),
Position = text.GetPointFromPosition(hint.Span.End),
TextEdits = ConvertToTextChanges(hint.ReplacementTextChange, text),
Data = (solutionVersionString, position)
});

Expand All @@ -152,6 +155,13 @@ public List<InlayHint> MapAndCacheHints(ImmutableArray<OmniSharpInlineHint> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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)
Expand Down Expand Up @@ -47,11 +52,13 @@ public async Task<(Document document, string documentPath)> GetAndAddExternalSym
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,16 @@ private async Task<Document> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ICodeActionProvider> providers,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -157,7 +168,7 @@ void decrementDocumentCountRemaining()

_workQueue.WorkComplete(workType);

await Task.Delay(50);
await Task.Delay(WorkerWait);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -219,7 +230,7 @@ public async Task<IEnumerable<Diagnostic>> 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);
}
Expand Down
Loading