Skip to content

Commit

Permalink
Clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi committed Dec 19, 2024
1 parent 632cfc4 commit 5e2c113
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 162 deletions.
151 changes: 53 additions & 98 deletions src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@

using System;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Tagging;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.CodeAnalysis.Utilities;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
Expand All @@ -20,28 +25,15 @@

namespace Microsoft.CodeAnalysis.Editor.InlineHints
{
using InlineHintTagCache = ImmutableDictionary<int, InlineHintTags>;

internal class InlineHintTags(TagSpan<InlineHintDataTag> dataTagSpan)
{
/// <summary>
/// Provided at creation time. Never changes.
/// </summary>
public readonly TagSpan<InlineHintDataTag> DataTagSpan = dataTagSpan;

/// <summary>
/// Created on demand when the adornment is needed.
/// </summary>
public TagSpan<IntraTextAdornmentTag>? AdornmentTagSpan;
}

/// <summary>
/// The purpose of this tagger is to convert the <see cref="InlineHintDataTag"/> to the <see
/// cref="InlineHintsTag"/>, which actually creates the UIElement. It reacts to tags changing and updates the
/// adornments accordingly.
/// </summary>
internal sealed class InlineHintsTagger : EfficientTagger<IntraTextAdornmentTag>
{
private static ConditionalWeakTable<TagSpan<InlineHintDataTag>, TagSpan<IntraTextAdornmentTag>> s_dataTagToAdornmentTag = new();

private readonly EfficientTagger<InlineHintDataTag> _underlyingTagger;

private readonly IClassificationFormatMap _formatMap;
Expand All @@ -55,63 +47,61 @@ internal sealed class InlineHintsTagger : EfficientTagger<IntraTextAdornmentTag>
private readonly InlineHintsTaggerProvider _taggerProvider;

private readonly IWpfTextView _textView;

private readonly object _gate = new();
/// <summary>
/// Stores the snapshot associated with the cached tags in <see cref="_cache_doNotAccessOutsideOfGate"/>.
/// Locked by <see cref="_gate"/>.
/// </summary>
private ITextSnapshot? _cacheSnapshot_doNotAccessOutsideOfGate;

/// <summary>
/// Mapping from position to the data tag computed for it, and the adornment tag (once we've computed that).
/// Locked by <see cref="_gate"/>.
/// </summary>
private InlineHintTagCache _cache_doNotAccessOutsideOfGate = InlineHintTagCache.Empty;
private readonly ITextBuffer _subjectBuffer;

public InlineHintsTagger(
InlineHintsTaggerProvider taggerProvider,
IWpfTextView textView,
ITextBuffer subjectBuffer,
EfficientTagger<InlineHintDataTag> tagger)
{
_taggerProvider = taggerProvider;

_textView = textView;
_subjectBuffer = subjectBuffer;

// When the underlying tagger produced new data tags, inform any clients of us that we have new adornment tags.
_underlyingTagger = tagger;
_underlyingTagger.TagsChanged += OnUnderlyingTagger_TagsChanged;
_underlyingTagger.TagsChanged += OnTagsChanged;

_formatMap = taggerProvider.ClassificationFormatMapService.GetClassificationFormatMap(textView);
_hintClassification = taggerProvider.ClassificationTypeRegistryService.GetClassificationType(InlineHintsTag.TagId);

_formatMap.ClassificationFormatMappingChanged += this.OnClassificationFormatMappingChanged;
_taggerProvider.GlobalOptionService.AddOptionChangedHandler(this, OnGlobalOptionChanged);
}

public override void Dispose()
{
_underlyingTagger.TagsChanged -= OnUnderlyingTagger_TagsChanged;
_underlyingTagger.Dispose();
_formatMap.ClassificationFormatMappingChanged -= OnClassificationFormatMappingChanged;
}

private void OnUnderlyingTagger_TagsChanged(object sender, SnapshotSpanEventArgs e)
{
InvalidateCache();
OnTagsChanged(this, e);
_taggerProvider.GlobalOptionService.RemoveOptionChangedHandler(this, OnGlobalOptionChanged);
_underlyingTagger.TagsChanged -= OnTagsChanged;
_underlyingTagger.Dispose();
}

private void OnClassificationFormatMappingChanged(object sender, EventArgs e)
{
_taggerProvider.ThreadingContext.ThrowIfNotOnUIThread();

// When classifications change we need to rebuild the inline tags with updated Font and Color information.

// Clear out the cached adornment tags we have associated with each data tag.
s_dataTagToAdornmentTag = new();

if (_format != null)
{
_format = null;
InvalidateCache();

// When classifications change we need to rebuild the inline tags with updated Font and Color information.
var tags = GetTags(new NormalizedSnapshotSpanCollection(_textView.TextViewLines.FormattedSpan));
OnTagsChanged(this, new SnapshotSpanEventArgs(_subjectBuffer.CurrentSnapshot.GetFullSpan()));
}
}

foreach (var tag in tags)
OnTagsChanged(this, new SnapshotSpanEventArgs(tag.Span));
private void OnGlobalOptionChanged(object sender, object target, OptionChangedEventArgs e)
{
if (e.HasOption(option => option.Equals(InlineHintsViewOptionsStorage.ColorHints)))
{
// Clear out cached adornments and reclassify everything.
s_dataTagToAdornmentTag = new();
OnTagsChanged(this, new SnapshotSpanEventArgs(_subjectBuffer.CurrentSnapshot.GetFullSpan()));
}
}

Expand All @@ -125,15 +115,6 @@ private TextFormattingRunProperties Format
}
}

private void InvalidateCache()
{
lock (_gate)
{
_cacheSnapshot_doNotAccessOutsideOfGate = null;
_cache_doNotAccessOutsideOfGate = InlineHintTagCache.Empty;
}
}

public override void AddTags(
NormalizedSnapshotSpanCollection spans,
SegmentedList<TagSpan<IntraTextAdornmentTag>> adornmentTagSpans)
Expand All @@ -143,22 +124,9 @@ public override void AddTags(
if (spans.Count == 0)
return;

ITextSnapshot? cacheSnapshot;
InlineHintTagCache cache;

lock (_gate)
{
cacheSnapshot = _cacheSnapshot_doNotAccessOutsideOfGate;
cache = _cache_doNotAccessOutsideOfGate;
}

var cacheBuilder = cache.ToBuilder();

// If the snapshot has changed, we can't use any of the cached data, as it is associated with the
// original snapshot they were created against.
var snapshot = spans[0].Snapshot;
if (snapshot != cacheSnapshot)
cacheBuilder.Clear();

var document = snapshot.GetOpenDocumentInCurrentContextWithChanges();
var classify = document != null && _taggerProvider.EditorOptionsService.GlobalOptions.GetOption(InlineHintsViewOptionsStorage.ColorHints, document.Project.Language);
Expand All @@ -173,34 +141,8 @@ public override void AddTags(

foreach (var dataTagSpan in dataTagSpans)
{
// Check if we already have a tag at this position. If not, initialize the cache to just point at
// the new data tag.
var position = dataTagSpan.Span.Start;
if (!cache.TryGetValue(position, out var inlineHintTags))
{
inlineHintTags = new(dataTagSpan);
cacheBuilder[position] = inlineHintTags;
}

if (seenPositions.Add(position))
{
// Now check if this is the first time we've been asked to compute the adornment for this particular
// data tag. If so, create and cache it so we don't recreate the adornments in the future for the
// same text snapshot.
//
// Note: creating the adornment doesn't change the cache itself. It just updates one of the values
// the cache is already pointing to. We only need to change the cache if we've added a new
// key/value mapping to it.
inlineHintTags.AdornmentTagSpan ??= CreateAdornmentTagSpan(inlineHintTags.DataTagSpan, classify);
adornmentTagSpans.Add(inlineHintTags.AdornmentTagSpan);
}
}

cache = cacheBuilder.ToImmutable();
lock (_gate)
{
_cacheSnapshot_doNotAccessOutsideOfGate = snapshot;
_cache_doNotAccessOutsideOfGate = cache;
if (seenPositions.Add(dataTagSpan.Span.Start))
adornmentTagSpans.Add(GetAdornmentTagsSpan(dataTagSpan, classify));
}
}
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.General))
Expand All @@ -209,15 +151,28 @@ public override void AddTags(
}
}

private TagSpan<IntraTextAdornmentTag> CreateAdornmentTagSpan(
private TagSpan<IntraTextAdornmentTag> GetAdornmentTagsSpan(
TagSpan<InlineHintDataTag> dataTagSpan, bool classify)
{
var adornmentSpan = dataTagSpan.Span;
if (s_dataTagToAdornmentTag.TryGetValue(dataTagSpan, out var adornmentTagSpan))
return adornmentTagSpan;

// Extracted as a helper method to avoid closure allocations when we find the adornment span in the map.
return GetOrCreateAdornmentTagSpan(dataTagSpan, classify);
}

private TagSpan<IntraTextAdornmentTag> GetOrCreateAdornmentTagSpan(
TagSpan<InlineHintDataTag> dataTagSpan, bool classify)
{
return s_dataTagToAdornmentTag.GetValue(dataTagSpan, dataTagSpan =>
{
var adornmentSpan = dataTagSpan.Span;

var hintUITag = InlineHintsTag.Create(
dataTagSpan.Tag.Hint, Format, _textView, adornmentSpan, _taggerProvider, _formatMap, classify);
var hintUITag = InlineHintsTag.Create(
dataTagSpan.Tag.Hint, Format, _textView, adornmentSpan, _taggerProvider, _formatMap, classify);

return new TagSpan<IntraTextAdornmentTag>(adornmentSpan, hintUITag);
return new TagSpan<IntraTextAdornmentTag>(adornmentSpan, hintUITag);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,62 +28,45 @@ namespace Microsoft.CodeAnalysis.Editor.InlineHints
[ContentType(ContentTypeNames.RoslynContentType)]
[TagType(typeof(IntraTextAdornmentTag))]
[Name(nameof(InlineHintsTaggerProvider))]
internal sealed class InlineHintsTaggerProvider : IViewTaggerProvider
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class InlineHintsTaggerProvider(
IGlobalOptionService globalOptionService,
IClassificationFormatMapService classificationFormatMapService,
IClassificationTypeRegistryService classificationTypeRegistryService,
IThreadingContext threadingContext,
IUIThreadOperationExecutor operationExecutor,
IAsynchronousOperationListenerProvider listenerProvider,
IToolTipService toolTipService,
ClassificationTypeMap typeMap,
Lazy<IStreamingFindUsagesPresenter> streamingFindUsagesPresenter,
EditorOptionsService editorOptionsService,
TaggerHost taggerHost,
[Import(AllowDefault = true)] IInlineHintKeyProcessor inlineHintKeyProcessor) : IViewTaggerProvider
{
// private readonly IViewTagAggregatorFactoryService _viewTagAggregatorFactoryService;
public readonly IClassificationFormatMapService ClassificationFormatMapService;
public readonly IClassificationTypeRegistryService ClassificationTypeRegistryService;
public readonly IThreadingContext ThreadingContext;
public readonly IUIThreadOperationExecutor OperationExecutor;
public readonly IAsynchronousOperationListener AsynchronousOperationListener;
public readonly IToolTipService ToolTipService;
public readonly ClassificationTypeMap TypeMap;
public readonly Lazy<IStreamingFindUsagesPresenter> StreamingFindUsagesPresenter;
public readonly EditorOptionsService EditorOptionsService;
public readonly IGlobalOptionService GlobalOptionService = globalOptionService;
public readonly IClassificationFormatMapService ClassificationFormatMapService = classificationFormatMapService;
public readonly IClassificationTypeRegistryService ClassificationTypeRegistryService = classificationTypeRegistryService;
public readonly IThreadingContext ThreadingContext = threadingContext;
public readonly IUIThreadOperationExecutor OperationExecutor = operationExecutor;
public readonly IAsynchronousOperationListener AsynchronousOperationListener = listenerProvider.GetListener(FeatureAttribute.InlineHints);
public readonly IToolTipService ToolTipService = toolTipService;
public readonly ClassificationTypeMap TypeMap = typeMap;
public readonly Lazy<IStreamingFindUsagesPresenter> StreamingFindUsagesPresenter = streamingFindUsagesPresenter;
public readonly EditorOptionsService EditorOptionsService = editorOptionsService;

private readonly InlineHintsDataTaggerProvider _dataTaggerProvider;
private readonly InlineHintsDataTaggerProvider _dataTaggerProvider = new(taggerHost, inlineHintKeyProcessor);

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public InlineHintsTaggerProvider(
// IViewTagAggregatorFactoryService viewTagAggregatorFactoryService,
IClassificationFormatMapService classificationFormatMapService,
IClassificationTypeRegistryService classificationTypeRegistryService,
IThreadingContext threadingContext,
IUIThreadOperationExecutor operationExecutor,
IAsynchronousOperationListenerProvider listenerProvider,
IToolTipService toolTipService,
ClassificationTypeMap typeMap,
Lazy<IStreamingFindUsagesPresenter> streamingFindUsagesPresenter,
EditorOptionsService editorOptionsService,
TaggerHost taggerHost,
[Import(AllowDefault = true)] IInlineHintKeyProcessor inlineHintKeyProcessor)
public ITagger<T>? CreateTagger<T>(ITextView textView, ITextBuffer subjectBuffer) where T : ITag
{
// _viewTagAggregatorFactoryService = viewTagAggregatorFactoryService;
ClassificationFormatMapService = classificationFormatMapService;
ClassificationTypeRegistryService = classificationTypeRegistryService;
ThreadingContext = threadingContext;
OperationExecutor = operationExecutor;
ToolTipService = toolTipService;
StreamingFindUsagesPresenter = streamingFindUsagesPresenter;
TypeMap = typeMap;
EditorOptionsService = editorOptionsService;

AsynchronousOperationListener = listenerProvider.GetListener(FeatureAttribute.InlineHints);

_dataTaggerProvider = new InlineHintsDataTaggerProvider(taggerHost, inlineHintKeyProcessor);
}

public ITagger<T>? CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
{
if (textView.IsNotSurfaceBufferOfTextView(buffer))
if (textView.IsNotSurfaceBufferOfTextView(subjectBuffer))
return null;

if (textView is not IWpfTextView wpfTextView)
return null;

var tagger = new InlineHintsTagger(
this, wpfTextView, _dataTaggerProvider.CreateTagger(textView, buffer));
this, wpfTextView, subjectBuffer, _dataTaggerProvider.CreateTagger(textView, subjectBuffer));
if (tagger is not ITagger<T> typedTagger)
{
tagger.Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@

using Microsoft.CodeAnalysis.Options;

namespace Microsoft.CodeAnalysis.Editor.InlineHints
namespace Microsoft.CodeAnalysis.Editor.InlineHints;

internal sealed class InlineHintsViewOptionsStorage
{
internal sealed class InlineHintsViewOptionsStorage
{
public static readonly Option2<bool> DisplayAllHintsWhilePressingAltF1 = new(
"dotnet_display_inline_hints_while_pressing_alt_f1", defaultValue: true);
public static readonly Option2<bool> DisplayAllHintsWhilePressingAltF1 = new(
"dotnet_display_inline_hints_while_pressing_alt_f1", defaultValue: true);

public static readonly PerLanguageOption2<bool> ColorHints = new(
"dotnet_colorize_inline_hints", defaultValue: true);
}
public static readonly PerLanguageOption2<bool> ColorHints = new(
"dotnet_colorize_inline_hints", defaultValue: true);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,25 @@
// 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.ComponentModel.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.Tagging;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.InlineHints;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
using VSUtilities = Microsoft.VisualStudio.Utilities;

namespace Microsoft.CodeAnalysis.Editor.InlineHints;

/// <summary>
/// The TaggerProvider that calls upon the service in order to locate the spans and names
/// </summary>
//[Export(typeof(IViewTaggerProvider))]
//[VSUtilities.ContentType(ContentTypeNames.RoslynContentType)]
//[TagType(typeof(InlineHintDataTag))]
//[VSUtilities.Name(nameof(InlineHintsDataTaggerProvider))]
//[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
//[method: ImportingConstructor]
internal sealed partial class InlineHintsDataTaggerProvider(
TaggerHost taggerHost,
IInlineHintKeyProcessor inlineHintKeyProcessor)
Expand Down

0 comments on commit 5e2c113

Please sign in to comment.