Skip to content

Commit

Permalink
Merge pull request #72409 from CyrusNajmabadi/simplifyInlineTagger
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi authored Mar 7, 2024
2 parents b073f7d + 4c6384c commit 3fa9ffd
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 403 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,63 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.InlineDiagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions;
using Microsoft.CodeAnalysis.Editor.UnitTests.Squiggles;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Text.Adornments;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InlineDiagnostics
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InlineDiagnostics;

[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.ErrorSquiggles), Trait(Traits.Feature, Traits.Features.Tagging)]
public class InlineDiagnosticsTaggerProviderTests
{
[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.ErrorSquiggles), Trait(Traits.Feature, Traits.Features.Tagging)]
public class InlineDiagnosticsTaggerProviderTests
[WpfFact]
public async Task ErrorTagGeneratedForError()
{
[WpfFact]
public async Task ErrorTagGeneratedForError()
{
var spans = await GetTagSpansAsync("class C {");
var firstSpan = Assert.Single(spans);
Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType);
}
var spans = await GetTagSpansAsync("class C {");
var firstSpan = Assert.Single(spans);
Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType);
}

[WpfFact]
public async Task ErrorTagGeneratedForErrorInSourceGeneratedDocument()
{
var spans = await GetTagSpansInSourceGeneratedDocumentAsync("class C {");
var firstSpan = Assert.Single(spans);
Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType);
}
[WpfFact]
public async Task ErrorTagGeneratedForErrorInSourceGeneratedDocument()
{
var spans = await GetTagSpansInSourceGeneratedDocumentAsync("class C {");
var firstSpan = Assert.Single(spans);
Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType);
}

private static async Task<ImmutableArray<ITagSpan<InlineDiagnosticsTag>>> GetTagSpansAsync(string content)
{
using var workspace = EditorTestWorkspace.CreateCSharp(content, composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler);
Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(
workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag), false))));
return await GetTagSpansAsync(workspace);
}
private static async Task<ImmutableArray<ITagSpan<InlineDiagnosticsTag>>> GetTagSpansAsync(string content)
{
using var workspace = EditorTestWorkspace.CreateCSharp(content, composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler);
Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(
workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag), false))));
return await GetTagSpansAsync(workspace);
}

private static async Task<ImmutableArray<ITagSpan<InlineDiagnosticsTag>>> GetTagSpansInSourceGeneratedDocumentAsync(string content)
{
using var workspace = EditorTestWorkspace.CreateCSharp(
files: [],
sourceGeneratedFiles: new[] { content },
composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler);
Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(
workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag), false))));
private static async Task<ImmutableArray<ITagSpan<InlineDiagnosticsTag>>> GetTagSpansInSourceGeneratedDocumentAsync(string content)
{
using var workspace = EditorTestWorkspace.CreateCSharp(
files: [],
sourceGeneratedFiles: new[] { content },
composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler);
Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(
workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag), false))));

return await GetTagSpansAsync(workspace);
}
return await GetTagSpansAsync(workspace);
}

private static async Task<ImmutableArray<ITagSpan<InlineDiagnosticsTag>>> GetTagSpansAsync(EditorTestWorkspace workspace)
{
workspace.GlobalOptions.SetGlobalOption(InlineDiagnosticsOptionsStorage.EnableInlineDiagnostics, LanguageNames.CSharp, true);
return (await TestDiagnosticTagProducer<InlineDiagnosticsTaggerProvider, InlineDiagnosticsTag>.GetDiagnosticsAndErrorSpans(workspace)).Item2;
}
private static async Task<ImmutableArray<ITagSpan<InlineDiagnosticsTag>>> GetTagSpansAsync(EditorTestWorkspace workspace)
{
workspace.GlobalOptions.SetGlobalOption(InlineDiagnosticsOptionsStorage.EnableInlineDiagnostics, LanguageNames.CSharp, true);
return (await TestDiagnosticTagProducer<InlineDiagnosticsTaggerProvider, InlineDiagnosticsTag>.GetDiagnosticsAndErrorSpans(workspace)).Item2;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ protected sealed override Task ProduceTagsAsync(
private async Task ProduceTagsAsync(
TaggerContext<TTag> context, DocumentSnapshotSpan documentSpanToTag, CancellationToken cancellationToken)
{
if (!_callback.IsEnabled)
return;

var document = documentSpanToTag.Document;
if (document == null)
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
Expand Down Expand Up @@ -63,12 +64,11 @@ SingleDiagnosticKindPullTaggerProvider CreateDiagnosticsTaggerProvider(Diagnosti
protected abstract ImmutableArray<IOption2> Options { get; }
protected virtual ImmutableArray<IOption2> FeatureOptions { get; } = [];

protected abstract bool IsEnabled { get; }

protected abstract bool IncludeDiagnostic(DiagnosticData data);

protected abstract bool TagEquals(TTag tag1, TTag tag2);
protected abstract ITagSpan<TTag>? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data);

protected abstract TTag? CreateTag(Workspace workspace, DiagnosticData diagnostic);

/// <summary>
/// Get the <see cref="DiagnosticDataLocation"/> that should have the tag applied to it.
Expand Down Expand Up @@ -111,4 +111,36 @@ private static ITaggerEventSource CreateEventSourceWorker(ITextBuffer subjectBuf
TaggerEventSources.OnDiagnosticsChanged(subjectBuffer, diagnosticService),
TaggerEventSources.OnTextChanged(subjectBuffer));
}

protected ITagSpan<TTag>? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data)
{
var errorTag = CreateTag(workspace, data);
if (errorTag == null)
return null;

// Ensure the diagnostic has at least length 1. Tags must have a non-empty length in order to actually show
// up in the editor.
var adjustedSpan = AdjustSnapshotSpan(span, minimumLength: 1);
if (adjustedSpan.Length == 0)
return null;

return new TagSpan<TTag>(adjustedSpan, errorTag);
}

protected virtual SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength)
=> AdjustSnapshotSpan(span, minimumLength, maximumLength: int.MaxValue);

protected static SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength, int maximumLength)
{
var snapshot = span.Snapshot;

// new length
var length = Math.Min(Math.Max(span.Length, minimumLength), maximumLength);

// make sure start + length is smaller than snapshot.Length and start is >= 0
var start = Math.Max(0, Math.Min(span.Start, snapshot.Length - length));

// make sure length is smaller than snapshot.Length which can happen if start == 0
return new SnapshotSpan(snapshot, start, Math.Min(start + length, snapshot.Length) - start);
}
}
Loading

0 comments on commit 3fa9ffd

Please sign in to comment.