Skip to content

Commit 5e2c113

Browse files
Clean up
1 parent 632cfc4 commit 5e2c113

File tree

4 files changed

+89
-162
lines changed

4 files changed

+89
-162
lines changed

src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs

Lines changed: 53 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44

55
using System;
66
using System.Collections.Immutable;
7+
using System.Linq;
8+
using System.Runtime.CompilerServices;
9+
using System.Runtime.InteropServices.ComTypes;
710
using Microsoft.CodeAnalysis.Collections;
811
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
912
using Microsoft.CodeAnalysis.Editor.Tagging;
1013
using Microsoft.CodeAnalysis.ErrorReporting;
14+
using Microsoft.CodeAnalysis.Options;
1115
using Microsoft.CodeAnalysis.PooledObjects;
1216
using Microsoft.CodeAnalysis.Text;
17+
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
1318
using Microsoft.CodeAnalysis.Utilities;
1419
using Microsoft.VisualStudio.Text;
1520
using Microsoft.VisualStudio.Text.Classification;
@@ -20,28 +25,15 @@
2025

2126
namespace Microsoft.CodeAnalysis.Editor.InlineHints
2227
{
23-
using InlineHintTagCache = ImmutableDictionary<int, InlineHintTags>;
24-
25-
internal class InlineHintTags(TagSpan<InlineHintDataTag> dataTagSpan)
26-
{
27-
/// <summary>
28-
/// Provided at creation time. Never changes.
29-
/// </summary>
30-
public readonly TagSpan<InlineHintDataTag> DataTagSpan = dataTagSpan;
31-
32-
/// <summary>
33-
/// Created on demand when the adornment is needed.
34-
/// </summary>
35-
public TagSpan<IntraTextAdornmentTag>? AdornmentTagSpan;
36-
}
37-
3828
/// <summary>
3929
/// The purpose of this tagger is to convert the <see cref="InlineHintDataTag"/> to the <see
4030
/// cref="InlineHintsTag"/>, which actually creates the UIElement. It reacts to tags changing and updates the
4131
/// adornments accordingly.
4232
/// </summary>
4333
internal sealed class InlineHintsTagger : EfficientTagger<IntraTextAdornmentTag>
4434
{
35+
private static ConditionalWeakTable<TagSpan<InlineHintDataTag>, TagSpan<IntraTextAdornmentTag>> s_dataTagToAdornmentTag = new();
36+
4537
private readonly EfficientTagger<InlineHintDataTag> _underlyingTagger;
4638

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

5749
private readonly IWpfTextView _textView;
58-
59-
private readonly object _gate = new();
60-
/// <summary>
61-
/// Stores the snapshot associated with the cached tags in <see cref="_cache_doNotAccessOutsideOfGate"/>.
62-
/// Locked by <see cref="_gate"/>.
63-
/// </summary>
64-
private ITextSnapshot? _cacheSnapshot_doNotAccessOutsideOfGate;
65-
66-
/// <summary>
67-
/// Mapping from position to the data tag computed for it, and the adornment tag (once we've computed that).
68-
/// Locked by <see cref="_gate"/>.
69-
/// </summary>
70-
private InlineHintTagCache _cache_doNotAccessOutsideOfGate = InlineHintTagCache.Empty;
50+
private readonly ITextBuffer _subjectBuffer;
7151

7252
public InlineHintsTagger(
7353
InlineHintsTaggerProvider taggerProvider,
7454
IWpfTextView textView,
55+
ITextBuffer subjectBuffer,
7556
EfficientTagger<InlineHintDataTag> tagger)
7657
{
7758
_taggerProvider = taggerProvider;
7859

7960
_textView = textView;
61+
_subjectBuffer = subjectBuffer;
8062

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

8467
_formatMap = taggerProvider.ClassificationFormatMapService.GetClassificationFormatMap(textView);
8568
_hintClassification = taggerProvider.ClassificationTypeRegistryService.GetClassificationType(InlineHintsTag.TagId);
69+
8670
_formatMap.ClassificationFormatMappingChanged += this.OnClassificationFormatMappingChanged;
71+
_taggerProvider.GlobalOptionService.AddOptionChangedHandler(this, OnGlobalOptionChanged);
8772
}
8873

8974
public override void Dispose()
9075
{
91-
_underlyingTagger.TagsChanged -= OnUnderlyingTagger_TagsChanged;
92-
_underlyingTagger.Dispose();
9376
_formatMap.ClassificationFormatMappingChanged -= OnClassificationFormatMappingChanged;
94-
}
95-
96-
private void OnUnderlyingTagger_TagsChanged(object sender, SnapshotSpanEventArgs e)
97-
{
98-
InvalidateCache();
99-
OnTagsChanged(this, e);
77+
_taggerProvider.GlobalOptionService.RemoveOptionChangedHandler(this, OnGlobalOptionChanged);
78+
_underlyingTagger.TagsChanged -= OnTagsChanged;
79+
_underlyingTagger.Dispose();
10080
}
10181

10282
private void OnClassificationFormatMappingChanged(object sender, EventArgs e)
10383
{
10484
_taggerProvider.ThreadingContext.ThrowIfNotOnUIThread();
85+
86+
// When classifications change we need to rebuild the inline tags with updated Font and Color information.
87+
88+
// Clear out the cached adornment tags we have associated with each data tag.
89+
s_dataTagToAdornmentTag = new();
90+
10591
if (_format != null)
10692
{
10793
_format = null;
108-
InvalidateCache();
109-
110-
// When classifications change we need to rebuild the inline tags with updated Font and Color information.
111-
var tags = GetTags(new NormalizedSnapshotSpanCollection(_textView.TextViewLines.FormattedSpan));
94+
OnTagsChanged(this, new SnapshotSpanEventArgs(_subjectBuffer.CurrentSnapshot.GetFullSpan()));
95+
}
96+
}
11297

113-
foreach (var tag in tags)
114-
OnTagsChanged(this, new SnapshotSpanEventArgs(tag.Span));
98+
private void OnGlobalOptionChanged(object sender, object target, OptionChangedEventArgs e)
99+
{
100+
if (e.HasOption(option => option.Equals(InlineHintsViewOptionsStorage.ColorHints)))
101+
{
102+
// Clear out cached adornments and reclassify everything.
103+
s_dataTagToAdornmentTag = new();
104+
OnTagsChanged(this, new SnapshotSpanEventArgs(_subjectBuffer.CurrentSnapshot.GetFullSpan()));
115105
}
116106
}
117107

@@ -125,15 +115,6 @@ private TextFormattingRunProperties Format
125115
}
126116
}
127117

128-
private void InvalidateCache()
129-
{
130-
lock (_gate)
131-
{
132-
_cacheSnapshot_doNotAccessOutsideOfGate = null;
133-
_cache_doNotAccessOutsideOfGate = InlineHintTagCache.Empty;
134-
}
135-
}
136-
137118
public override void AddTags(
138119
NormalizedSnapshotSpanCollection spans,
139120
SegmentedList<TagSpan<IntraTextAdornmentTag>> adornmentTagSpans)
@@ -143,22 +124,9 @@ public override void AddTags(
143124
if (spans.Count == 0)
144125
return;
145126

146-
ITextSnapshot? cacheSnapshot;
147-
InlineHintTagCache cache;
148-
149-
lock (_gate)
150-
{
151-
cacheSnapshot = _cacheSnapshot_doNotAccessOutsideOfGate;
152-
cache = _cache_doNotAccessOutsideOfGate;
153-
}
154-
155-
var cacheBuilder = cache.ToBuilder();
156-
157127
// If the snapshot has changed, we can't use any of the cached data, as it is associated with the
158128
// original snapshot they were created against.
159129
var snapshot = spans[0].Snapshot;
160-
if (snapshot != cacheSnapshot)
161-
cacheBuilder.Clear();
162130

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

174142
foreach (var dataTagSpan in dataTagSpans)
175143
{
176-
// Check if we already have a tag at this position. If not, initialize the cache to just point at
177-
// the new data tag.
178-
var position = dataTagSpan.Span.Start;
179-
if (!cache.TryGetValue(position, out var inlineHintTags))
180-
{
181-
inlineHintTags = new(dataTagSpan);
182-
cacheBuilder[position] = inlineHintTags;
183-
}
184-
185-
if (seenPositions.Add(position))
186-
{
187-
// Now check if this is the first time we've been asked to compute the adornment for this particular
188-
// data tag. If so, create and cache it so we don't recreate the adornments in the future for the
189-
// same text snapshot.
190-
//
191-
// Note: creating the adornment doesn't change the cache itself. It just updates one of the values
192-
// the cache is already pointing to. We only need to change the cache if we've added a new
193-
// key/value mapping to it.
194-
inlineHintTags.AdornmentTagSpan ??= CreateAdornmentTagSpan(inlineHintTags.DataTagSpan, classify);
195-
adornmentTagSpans.Add(inlineHintTags.AdornmentTagSpan);
196-
}
197-
}
198-
199-
cache = cacheBuilder.ToImmutable();
200-
lock (_gate)
201-
{
202-
_cacheSnapshot_doNotAccessOutsideOfGate = snapshot;
203-
_cache_doNotAccessOutsideOfGate = cache;
144+
if (seenPositions.Add(dataTagSpan.Span.Start))
145+
adornmentTagSpans.Add(GetAdornmentTagsSpan(dataTagSpan, classify));
204146
}
205147
}
206148
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.General))
@@ -209,15 +151,28 @@ public override void AddTags(
209151
}
210152
}
211153

212-
private TagSpan<IntraTextAdornmentTag> CreateAdornmentTagSpan(
154+
private TagSpan<IntraTextAdornmentTag> GetAdornmentTagsSpan(
213155
TagSpan<InlineHintDataTag> dataTagSpan, bool classify)
214156
{
215-
var adornmentSpan = dataTagSpan.Span;
157+
if (s_dataTagToAdornmentTag.TryGetValue(dataTagSpan, out var adornmentTagSpan))
158+
return adornmentTagSpan;
159+
160+
// Extracted as a helper method to avoid closure allocations when we find the adornment span in the map.
161+
return GetOrCreateAdornmentTagSpan(dataTagSpan, classify);
162+
}
163+
164+
private TagSpan<IntraTextAdornmentTag> GetOrCreateAdornmentTagSpan(
165+
TagSpan<InlineHintDataTag> dataTagSpan, bool classify)
166+
{
167+
return s_dataTagToAdornmentTag.GetValue(dataTagSpan, dataTagSpan =>
168+
{
169+
var adornmentSpan = dataTagSpan.Span;
216170

217-
var hintUITag = InlineHintsTag.Create(
218-
dataTagSpan.Tag.Hint, Format, _textView, adornmentSpan, _taggerProvider, _formatMap, classify);
171+
var hintUITag = InlineHintsTag.Create(
172+
dataTagSpan.Tag.Hint, Format, _textView, adornmentSpan, _taggerProvider, _formatMap, classify);
219173

220-
return new TagSpan<IntraTextAdornmentTag>(adornmentSpan, hintUITag);
174+
return new TagSpan<IntraTextAdornmentTag>(adornmentSpan, hintUITag);
175+
});
221176
}
222177
}
223178
}

src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTaggerProvider.cs

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -28,62 +28,45 @@ namespace Microsoft.CodeAnalysis.Editor.InlineHints
2828
[ContentType(ContentTypeNames.RoslynContentType)]
2929
[TagType(typeof(IntraTextAdornmentTag))]
3030
[Name(nameof(InlineHintsTaggerProvider))]
31-
internal sealed class InlineHintsTaggerProvider : IViewTaggerProvider
31+
[method: ImportingConstructor]
32+
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
33+
internal sealed class InlineHintsTaggerProvider(
34+
IGlobalOptionService globalOptionService,
35+
IClassificationFormatMapService classificationFormatMapService,
36+
IClassificationTypeRegistryService classificationTypeRegistryService,
37+
IThreadingContext threadingContext,
38+
IUIThreadOperationExecutor operationExecutor,
39+
IAsynchronousOperationListenerProvider listenerProvider,
40+
IToolTipService toolTipService,
41+
ClassificationTypeMap typeMap,
42+
Lazy<IStreamingFindUsagesPresenter> streamingFindUsagesPresenter,
43+
EditorOptionsService editorOptionsService,
44+
TaggerHost taggerHost,
45+
[Import(AllowDefault = true)] IInlineHintKeyProcessor inlineHintKeyProcessor) : IViewTaggerProvider
3246
{
33-
// private readonly IViewTagAggregatorFactoryService _viewTagAggregatorFactoryService;
34-
public readonly IClassificationFormatMapService ClassificationFormatMapService;
35-
public readonly IClassificationTypeRegistryService ClassificationTypeRegistryService;
36-
public readonly IThreadingContext ThreadingContext;
37-
public readonly IUIThreadOperationExecutor OperationExecutor;
38-
public readonly IAsynchronousOperationListener AsynchronousOperationListener;
39-
public readonly IToolTipService ToolTipService;
40-
public readonly ClassificationTypeMap TypeMap;
41-
public readonly Lazy<IStreamingFindUsagesPresenter> StreamingFindUsagesPresenter;
42-
public readonly EditorOptionsService EditorOptionsService;
47+
public readonly IGlobalOptionService GlobalOptionService = globalOptionService;
48+
public readonly IClassificationFormatMapService ClassificationFormatMapService = classificationFormatMapService;
49+
public readonly IClassificationTypeRegistryService ClassificationTypeRegistryService = classificationTypeRegistryService;
50+
public readonly IThreadingContext ThreadingContext = threadingContext;
51+
public readonly IUIThreadOperationExecutor OperationExecutor = operationExecutor;
52+
public readonly IAsynchronousOperationListener AsynchronousOperationListener = listenerProvider.GetListener(FeatureAttribute.InlineHints);
53+
public readonly IToolTipService ToolTipService = toolTipService;
54+
public readonly ClassificationTypeMap TypeMap = typeMap;
55+
public readonly Lazy<IStreamingFindUsagesPresenter> StreamingFindUsagesPresenter = streamingFindUsagesPresenter;
56+
public readonly EditorOptionsService EditorOptionsService = editorOptionsService;
4357

44-
private readonly InlineHintsDataTaggerProvider _dataTaggerProvider;
58+
private readonly InlineHintsDataTaggerProvider _dataTaggerProvider = new(taggerHost, inlineHintKeyProcessor);
4559

46-
[ImportingConstructor]
47-
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
48-
public InlineHintsTaggerProvider(
49-
// IViewTagAggregatorFactoryService viewTagAggregatorFactoryService,
50-
IClassificationFormatMapService classificationFormatMapService,
51-
IClassificationTypeRegistryService classificationTypeRegistryService,
52-
IThreadingContext threadingContext,
53-
IUIThreadOperationExecutor operationExecutor,
54-
IAsynchronousOperationListenerProvider listenerProvider,
55-
IToolTipService toolTipService,
56-
ClassificationTypeMap typeMap,
57-
Lazy<IStreamingFindUsagesPresenter> streamingFindUsagesPresenter,
58-
EditorOptionsService editorOptionsService,
59-
TaggerHost taggerHost,
60-
[Import(AllowDefault = true)] IInlineHintKeyProcessor inlineHintKeyProcessor)
60+
public ITagger<T>? CreateTagger<T>(ITextView textView, ITextBuffer subjectBuffer) where T : ITag
6161
{
62-
// _viewTagAggregatorFactoryService = viewTagAggregatorFactoryService;
63-
ClassificationFormatMapService = classificationFormatMapService;
64-
ClassificationTypeRegistryService = classificationTypeRegistryService;
65-
ThreadingContext = threadingContext;
66-
OperationExecutor = operationExecutor;
67-
ToolTipService = toolTipService;
68-
StreamingFindUsagesPresenter = streamingFindUsagesPresenter;
69-
TypeMap = typeMap;
70-
EditorOptionsService = editorOptionsService;
71-
72-
AsynchronousOperationListener = listenerProvider.GetListener(FeatureAttribute.InlineHints);
73-
74-
_dataTaggerProvider = new InlineHintsDataTaggerProvider(taggerHost, inlineHintKeyProcessor);
75-
}
76-
77-
public ITagger<T>? CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
78-
{
79-
if (textView.IsNotSurfaceBufferOfTextView(buffer))
62+
if (textView.IsNotSurfaceBufferOfTextView(subjectBuffer))
8063
return null;
8164

8265
if (textView is not IWpfTextView wpfTextView)
8366
return null;
8467

8568
var tagger = new InlineHintsTagger(
86-
this, wpfTextView, _dataTaggerProvider.CreateTagger(textView, buffer));
69+
this, wpfTextView, subjectBuffer, _dataTaggerProvider.CreateTagger(textView, subjectBuffer));
8770
if (tagger is not ITagger<T> typedTagger)
8871
{
8972
tagger.Dispose();

src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsViewOptionsStorage.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44

55
using Microsoft.CodeAnalysis.Options;
66

7-
namespace Microsoft.CodeAnalysis.Editor.InlineHints
7+
namespace Microsoft.CodeAnalysis.Editor.InlineHints;
8+
9+
internal sealed class InlineHintsViewOptionsStorage
810
{
9-
internal sealed class InlineHintsViewOptionsStorage
10-
{
11-
public static readonly Option2<bool> DisplayAllHintsWhilePressingAltF1 = new(
12-
"dotnet_display_inline_hints_while_pressing_alt_f1", defaultValue: true);
11+
public static readonly Option2<bool> DisplayAllHintsWhilePressingAltF1 = new(
12+
"dotnet_display_inline_hints_while_pressing_alt_f1", defaultValue: true);
1313

14-
public static readonly PerLanguageOption2<bool> ColorHints = new(
15-
"dotnet_colorize_inline_hints", defaultValue: true);
16-
}
14+
public static readonly PerLanguageOption2<bool> ColorHints = new(
15+
"dotnet_colorize_inline_hints", defaultValue: true);
1716
}

src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,25 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using System;
6-
using System.ComponentModel.Composition;
75
using System.Threading;
86
using System.Threading.Tasks;
97
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
108
using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
119
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
1210
using Microsoft.CodeAnalysis.Editor.Tagging;
13-
using Microsoft.CodeAnalysis.Host.Mef;
1411
using Microsoft.CodeAnalysis.InlineHints;
1512
using Microsoft.CodeAnalysis.Shared.Extensions;
1613
using Microsoft.CodeAnalysis.Shared.TestHooks;
1714
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
1815
using Microsoft.VisualStudio.Text;
1916
using Microsoft.VisualStudio.Text.Editor;
2017
using Microsoft.VisualStudio.Text.Tagging;
21-
using VSUtilities = Microsoft.VisualStudio.Utilities;
2218

2319
namespace Microsoft.CodeAnalysis.Editor.InlineHints;
2420

2521
/// <summary>
2622
/// The TaggerProvider that calls upon the service in order to locate the spans and names
2723
/// </summary>
28-
//[Export(typeof(IViewTaggerProvider))]
29-
//[VSUtilities.ContentType(ContentTypeNames.RoslynContentType)]
30-
//[TagType(typeof(InlineHintDataTag))]
31-
//[VSUtilities.Name(nameof(InlineHintsDataTaggerProvider))]
32-
//[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
33-
//[method: ImportingConstructor]
3424
internal sealed partial class InlineHintsDataTaggerProvider(
3525
TaggerHost taggerHost,
3626
IInlineHintKeyProcessor inlineHintKeyProcessor)

0 commit comments

Comments
 (0)