<commit_before>﻿// Licensed to the .NET Foundation under one or more agreements.
// 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.Generic;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Editor.InlineHints
{
    /// <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 : ITagger<IntraTextAdornmentTag>, IDisposable
    {
        private readonly ITagAggregator<InlineHintDataTag> _tagAggregator;

        /// <summary>
        /// stores the parameter hint tags in a global location
        /// </summary>
        private readonly List<(IMappingTagSpan<InlineHintDataTag> mappingTagSpan, ITagSpan<IntraTextAdornmentTag>? tagSpan)> _cache = new();

        /// <summary>
        /// Stores the snapshot associated with the cached tags in <see cref="_cache" />
        /// </summary>
        private ITextSnapshot? _cacheSnapshot;

        private readonly IClassificationFormatMap _formatMap;

        /// <summary>
        /// Lazy initialized, use <see cref="Format"/> for access
        /// </summary>
        private TextFormattingRunProperties? _format;
        private readonly IClassificationType _hintClassification;

        private readonly InlineHintsTaggerProvider _taggerProvider;

        private readonly ITextBuffer _buffer;
        private readonly IWpfTextView _textView;

        public event EventHandler<SnapshotSpanEventArgs>? TagsChanged;

        public InlineHintsTagger(
            InlineHintsTaggerProvider taggerProvider,
            IWpfTextView textView,
            ITextBuffer buffer,
            ITagAggregator<InlineHintDataTag> tagAggregator)
        {
            _taggerProvider = taggerProvider;

            _textView = textView;
            _buffer = buffer;

            _tagAggregator = tagAggregator;
            _formatMap = taggerProvider.ClassificationFormatMapService.GetClassificationFormatMap(textView);
            _hintClassification = taggerProvider.ClassificationTypeRegistryService.GetClassificationType(InlineHintsTag.TagId);
            _formatMap.ClassificationFormatMappingChanged += this.OnClassificationFormatMappingChanged;
            _tagAggregator.BatchedTagsChanged += TagAggregator_BatchedTagsChanged;
        }

        /// <summary>
        /// Goes through all the spans in which tags have changed and
        /// invokes a TagsChanged event. Using the BatchedTagsChangedEvent since it is raised
        /// on the same thread that created the tag aggregator, unlike TagsChanged.
        /// </summary>
        private void TagAggregator_BatchedTagsChanged(object sender, BatchedTagsChangedEventArgs e)
        {
            _taggerProvider.ThreadingContext.ThrowIfNotOnUIThread();
            InvalidateCache();

            var mappingSpans = e.Spans;
            foreach (var item in mappingSpans)
            {
                var spans = item.GetSpans(_buffer);
                foreach (var span in spans)
                {
                    var tagsChanged = TagsChanged;
                    if (tagsChanged != null)
                    {
                        tagsChanged.Invoke(this, new SnapshotSpanEventArgs(span));
                    }
                }
            }
        }

        private void OnClassificationFormatMappingChanged(object sender, EventArgs e)
        {
            _taggerProvider.ThreadingContext.ThrowIfNotOnUIThread();
            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));

                foreach (var tag in tags)
                {
                    TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(tag.Span));
                }
            }
        }

        private TextFormattingRunProperties Format
        {
            get
            {
                _taggerProvider.ThreadingContext.ThrowIfNotOnUIThread();
                _format ??= _formatMap.GetTextProperties(_hintClassification);
                return _format;
            }
        }

        private void InvalidateCache()
        {
            _cacheSnapshot = null;
            _cache.Clear();
        }

        public IEnumerable<ITagSpan<IntraTextAdornmentTag>> GetTags(NormalizedSnapshotSpanCollection spans)
        {
            try
            {
                if (spans.Count == 0)
                {
                    return Array.Empty<ITagSpan<IntraTextAdornmentTag>>();
                }

                var snapshot = spans[0].Snapshot;
                if (snapshot != _cacheSnapshot)
                {
                    // Calculate UI elements<commit_msg>feedback<commit_after>﻿// Licensed to the .NET Foundation under one or more agreements.
// 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.Generic;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Editor.InlineHints
{
    /// <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 : ITagger<IntraTextAdornmentTag>, IDisposable
    {
        private readonly ITagAggregator<InlineHintDataTag> _tagAggregator;

        /// <summary>
        /// stores the parameter hint tags in a global location
        /// </summary>
        private readonly List<(IMappingTagSpan<InlineHintDataTag> mappingTagSpan, ITagSpan<IntraTextAdornmentTag>? tagSpan)> _cache = new();

        /// <summary>
        /// Stores the snapshot associated with the cached tags in <see cref="_cache" />
        /// </summary>
        private ITextSnapshot? _cacheSnapshot;

        private readonly IClassificationFormatMap _formatMap;

        /// <summary>
        /// Lazy initialized, use <see cref="Format"/> for access
        /// </summary>
        private TextFormattingRunProperties? _format;
        private readonly IClassificationType _hintClassification;

        private readonly InlineHintsTaggerProvider _taggerProvider;

        private readonly ITextBuffer _buffer;
        private readonly IWpfTextView _textView;

        public event EventHandler<SnapshotSpanEventArgs>? TagsChanged;

        public InlineHintsTagger(
            InlineHintsTaggerProvider taggerProvider,
            IWpfTextView textView,
            ITextBuffer buffer,
            ITagAggregator<InlineHintDataTag> tagAggregator)
        {
            _taggerProvider = taggerProvider;

            _textView = textView;
            _buffer = buffer;

            _tagAggregator = tagAggregator;
            _formatMap = taggerProvider.ClassificationFormatMapService.GetClassificationFormatMap(textView);
            _hintClassification = taggerProvider.ClassificationTypeRegistryService.GetClassificationType(InlineHintsTag.TagId);
            _formatMap.ClassificationFormatMappingChanged += this.OnClassificationFormatMappingChanged;
            _tagAggregator.BatchedTagsChanged += TagAggregator_BatchedTagsChanged;
        }

        /// <summary>
        /// Goes through all the spans in which tags have changed and
        /// invokes a TagsChanged event. Using the BatchedTagsChangedEvent since it is raised
        /// on the same thread that created the tag aggregator, unlike TagsChanged.
        /// </summary>
        private void TagAggregator_BatchedTagsChanged(object sender, BatchedTagsChangedEventArgs e)
        {
            _taggerProvider.ThreadingContext.ThrowIfNotOnUIThread();
            InvalidateCache();

            var tagsChanged = TagsChanged;
            if (tagsChanged is null)
            {
                return;
            }

            var mappingSpans = e.Spans;
            foreach (var item in mappingSpans)
            {
                var spans = item.GetSpans(_buffer);
                foreach (var span in spans)
                {
                    if (tagsChanged != null)
                    {
                        tagsChanged.Invoke(this, new SnapshotSpanEventArgs(span));
                    }
                }
            }
        }

        private void OnClassificationFormatMappingChanged(object sender, EventArgs e)
        {
            _taggerProvider.ThreadingContext.ThrowIfNotOnUIThread();
            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));

                foreach (var tag in tags)
                {
                    TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(tag.Span));
                }
            }
        }

        private TextFormattingRunProperties Format
        {
            get
            {
                _taggerProvider.ThreadingContext.ThrowIfNotOnUIThread();
                _format ??= _formatMap.GetTextProperties(_hintClassification);
                return _format;
            }
        }

        private void InvalidateCache()
        {
            _taggerProvider.ThreadingContext.ThrowIfNotOnUIThread();
            _cacheSnapshot = null;
            _cache.Clear();
        }

        public IEnumerable<ITagSpan<IntraTextAdornmentTag>> GetTags(NormalizedSnapshotSpanCollection spans)
        {
            try
            {
                if (spans.Count == 0)
                {
                    return Array.Empty<ITagSpan<IntraTextAdornmentTag>>();
                }

                var snapshot = spans[0].Snapshot;
                if (snapshot != _cacheSnapshot)
                {
                    // Calculate UI elements
{'fraction_non_alphanumeric': 0.0712367997136209, 'fraction_numerical': 0.00035797386790764276, 'mean_word_length': 2.1093489148580966, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3832965', 'n_tokens_mistral': 2749, 'n_tokens_neox': 2647, 'n_words': 685}