Skip to content

Commit f70d25b

Browse files
Simplify
1 parent f1a2f7f commit f70d25b

File tree

3 files changed

+38
-39
lines changed

3 files changed

+38
-39
lines changed

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

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,27 @@
2020
using Microsoft.VisualStudio.Text.Tagging;
2121
using Roslyn.Utilities;
2222

23-
namespace Microsoft.CodeAnalysis.Editor.InlineHints
23+
namespace Microsoft.CodeAnalysis.Editor.InlineHints;
24+
25+
internal partial class InlineHintsTaggerProvider
2426
{
27+
private sealed class AdornmentTagInformation(
28+
bool Classified,
29+
TextFormattingRunProperties Format,
30+
TagSpan<IntraTextAdornmentTag> AdornmentTagSpan)
31+
{
32+
public bool Classified { get; } = Classified;
33+
public TextFormattingRunProperties Format { get; } = Format;
34+
public TagSpan<IntraTextAdornmentTag> AdornmentTagSpan { get; } = AdornmentTagSpan;
35+
}
36+
2537
/// <summary>
2638
/// The purpose of this tagger is to convert the <see cref="InlineHintDataTag"/> to the <see
2739
/// cref="InlineHintsTag"/>, which actually creates the UIElement. It reacts to tags changing and updates the
2840
/// adornments accordingly.
2941
/// </summary>
30-
internal sealed class InlineHintsTagger : EfficientTagger<IntraTextAdornmentTag>
42+
private sealed class InlineHintsTagger : EfficientTagger<IntraTextAdornmentTag>
3143
{
32-
/// <summary>
33-
/// On demand mapping of data tags to adornment tags created for them. As long as the data tags are alive,
34-
/// we'll keep the corresponding adornment tags alive <em>once it has been created</em>. Note: the underlying
35-
/// tagger is a view tagger, which will toss tags once they get far enough out of view. So we will only create
36-
/// and cache as many adornment tags as what the underlying tagger thinks there should be tags for <em>and</em>
37-
/// which have been requested by the view tagger.
38-
/// </summary>
39-
private static ConditionalWeakTable<TagSpan<InlineHintDataTag>, TagSpan<IntraTextAdornmentTag>> s_dataTagToAdornmentTag = new();
40-
4144
private readonly EfficientTagger<InlineHintDataTag> _underlyingTagger;
4245

4346
private readonly IClassificationFormatMap _formatMap;
@@ -89,9 +92,6 @@ private void OnClassificationFormatMappingChanged(object sender, EventArgs e)
8992

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

92-
// Clear out the cached adornment tags we have associated with each data tag.
93-
s_dataTagToAdornmentTag = new();
94-
9595
if (_format != null)
9696
{
9797
_format = null;
@@ -101,12 +101,9 @@ private void OnClassificationFormatMappingChanged(object sender, EventArgs e)
101101

102102
private void OnGlobalOptionChanged(object sender, object target, OptionChangedEventArgs e)
103103
{
104+
// Reclassify everything.
104105
if (e.HasOption(option => option.Equals(InlineHintsViewOptionsStorage.ColorHints)))
105-
{
106-
// Clear out cached adornments and reclassify everything.
107-
s_dataTagToAdornmentTag = new();
108106
OnTagsChanged(this, new SnapshotSpanEventArgs(_subjectBuffer.CurrentSnapshot.GetFullSpan()));
109-
}
110107
}
111108

112109
private TextFormattingRunProperties Format
@@ -133,7 +130,10 @@ public override void AddTags(
133130
var snapshot = spans[0].Snapshot;
134131

135132
var document = snapshot.GetOpenDocumentInCurrentContextWithChanges();
136-
var classify = document != null && _taggerProvider.EditorOptionsService.GlobalOptions.GetOption(InlineHintsViewOptionsStorage.ColorHints, document.Project.Language);
133+
if (document is null)
134+
return;
135+
136+
var colorHints = _taggerProvider.EditorOptionsService.GlobalOptions.GetOption(InlineHintsViewOptionsStorage.ColorHints, document.Project.Language);
137137

138138
using var _1 = SegmentedListPool.GetPooledList<TagSpan<InlineHintDataTag>>(out var dataTagSpans);
139139
_underlyingTagger.AddTags(spans, dataTagSpans);
@@ -143,10 +143,11 @@ public override void AddTags(
143143

144144
using var _2 = PooledHashSet<int>.GetInstance(out var seenPositions);
145145

146+
var format = this.Format;
146147
foreach (var dataTagSpan in dataTagSpans)
147148
{
148149
if (seenPositions.Add(dataTagSpan.Span.Start))
149-
adornmentTagSpans.Add(GetAdornmentTagsSpan(dataTagSpan, classify));
150+
adornmentTagSpans.Add(GetOrCreateAdornmentTagsSpan(dataTagSpan, colorHints, format));
150151
}
151152
}
152153
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.General))
@@ -155,28 +156,20 @@ public override void AddTags(
155156
}
156157
}
157158

158-
private TagSpan<IntraTextAdornmentTag> GetAdornmentTagsSpan(
159-
TagSpan<InlineHintDataTag> dataTagSpan, bool classify)
159+
private TagSpan<IntraTextAdornmentTag> GetOrCreateAdornmentTagsSpan(
160+
TagSpan<InlineHintDataTag> dataTagSpan, bool classify, TextFormattingRunProperties format)
160161
{
161-
if (s_dataTagToAdornmentTag.TryGetValue(dataTagSpan, out var adornmentTagSpan))
162-
return adornmentTagSpan;
163-
164-
// Extracted as a helper method to avoid closure allocations when we find the adornment span in the map.
165-
return GetOrCreateAdornmentTagSpan(dataTagSpan, classify);
166-
}
167-
168-
private TagSpan<IntraTextAdornmentTag> GetOrCreateAdornmentTagSpan(
169-
TagSpan<InlineHintDataTag> dataTagSpan, bool classify)
170-
{
171-
return s_dataTagToAdornmentTag.GetValue(dataTagSpan, dataTagSpan =>
162+
// If we've never computed the adornment info, or options have changed, then compute and cache the new information.
163+
var cachedTagInformation = (AdornmentTagInformation?)dataTagSpan.Tag.AdditionalData;
164+
if (cachedTagInformation is null || cachedTagInformation.Classified != classify || cachedTagInformation.Format != format)
172165
{
173166
var adornmentSpan = dataTagSpan.Span;
167+
cachedTagInformation = new(classify, format, new TagSpan<IntraTextAdornmentTag>(adornmentSpan, InlineHintsTag.Create(
168+
dataTagSpan.Tag.Hint, format, _textView, adornmentSpan, _taggerProvider, _formatMap, classify)));
169+
dataTagSpan.Tag.AdditionalData = cachedTagInformation;
170+
}
174171

175-
var hintUITag = InlineHintsTag.Create(
176-
dataTagSpan.Tag.Hint, Format, _textView, adornmentSpan, _taggerProvider, _formatMap, classify);
177-
178-
return new TagSpan<IntraTextAdornmentTag>(adornmentSpan, hintUITag);
179-
});
172+
return cachedTagInformation.AdornmentTagSpan;
180173
}
181174
}
182175
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace Microsoft.CodeAnalysis.Editor.InlineHints
3030
[Name(nameof(InlineHintsTaggerProvider))]
3131
[method: ImportingConstructor]
3232
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
33-
internal sealed class InlineHintsTaggerProvider(
33+
internal sealed partial class InlineHintsTaggerProvider(
3434
IGlobalOptionService globalOptionService,
3535
IClassificationFormatMapService classificationFormatMapService,
3636
IClassificationTypeRegistryService classificationTypeRegistryService,

src/EditorFeatures/Core/InlineHints/InlineHintDataTag.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ internal sealed class InlineHintDataTag(InlineHintsDataTaggerProvider provider,
2727

2828
public readonly InlineHint Hint = hint;
2929

30+
/// <summary>
31+
/// Additional data that can be attached to the tag. For example, the view tagger uses this to attach the adornment
32+
/// tag information so it can be created and cached on demand.
33+
/// </summary>
34+
public object? AdditionalData;
35+
3036
// Intentionally throwing, we have never supported this facility, and there is no contract around placing
3137
// these tags in sets or maps.
3238
public override int GetHashCode()

0 commit comments

Comments
 (0)