// // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. // // This file contain implementations details that are subject to change without notice. // Use at your own risk. // namespace Microsoft.VisualStudio.Text.Tagging.Implementation { using System; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Linq; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Projection; using Microsoft.VisualStudio.Text.Utilities; using Microsoft.VisualStudio.Threading; /// /// Exports the TagAggregator provider, both the buffer and view version. /// [Export(typeof(IBufferTagAggregatorFactoryService))] [Export(typeof(IViewTagAggregatorFactoryService))] internal sealed class TagAggregatorFactoryService : IBufferTagAggregatorFactoryService, IViewTagAggregatorFactoryService { [ImportMany(typeof(ITaggerProvider))] internal List> BufferTaggerProviders { get; set; } [ImportMany(typeof(IViewTaggerProvider))] internal List> ViewTaggerProviders { get; set; } [Import] internal IBufferGraphFactoryService BufferGraphFactoryService { get; set; } [Import] internal IContentTypeRegistryService ContentTypeRegistryService { get; set; } [Import] internal JoinableTaskContext JoinableTaskContext { get; set; } [Import] internal GuardedOperations GuardedOperations { get; set; } internal ImmutableDictionary>> _bufferTaggerProviderMap = ImmutableDictionary>>.Empty; internal ImmutableDictionary>> _viewTaggerProviderMap = ImmutableDictionary>>.Empty; #region IBufferTagAggregatorFactoryService Members public ITagAggregator CreateTagAggregator(ITextBuffer textBuffer) where T : ITag { return CreateTagAggregator(textBuffer, TagAggregatorOptions.None); } public ITagAggregator CreateTagAggregator(ITextBuffer textBuffer, TagAggregatorOptions options) where T : ITag { if (textBuffer == null) throw new ArgumentNullException("textBuffer"); return new TagAggregator(this, null, this.BufferGraphFactoryService.CreateBufferGraph(textBuffer), options); } #endregion #region IViewTagAggregatorFactoryService Members public ITagAggregator CreateTagAggregator(ITextView textView) where T : ITag { return CreateTagAggregator(textView, TagAggregatorOptions.None); } public ITagAggregator CreateTagAggregator(ITextView textView, TagAggregatorOptions options) where T : ITag { if (textView == null) throw new ArgumentNullException("textView"); return new TagAggregator(this, textView, textView.BufferGraph, options); } #endregion internal IEnumerable> GetBufferTaggersForType(IContentType type, Type taggerType) { var key = new ContentAndTypeData(type, taggerType); IEnumerable> taggers; if (!_bufferTaggerProviderMap.TryGetValue(key, out taggers)) { taggers = new List>(this.BufferTaggerProviders.Where(f => Match(type, taggerType, f.Metadata))); ImmutableInterlocked.Update(ref _bufferTaggerProviderMap, (s) => s.Add(key, taggers)); } return taggers; } internal IEnumerable> GetViewTaggersForType(IContentType type, Type taggerType) { var key = new ContentAndTypeData(type, taggerType); IEnumerable> taggers; if (!_viewTaggerProviderMap.TryGetValue(key, out taggers)) { taggers = new List>(this.ViewTaggerProviders.Where(f => Match(type, taggerType, f.Metadata))); ImmutableInterlocked.Update(ref _viewTaggerProviderMap, (s) => s.Add(key, taggers)); } return taggers; } private static bool Match(IContentType bufferContentType, Type taggerType, INamedTaggerMetadata tagMetadata) { bool contentTypeMatch = false; foreach (string contentType in tagMetadata.ContentTypes) { if (bufferContentType.IsOfType(contentType)) { contentTypeMatch = true; break; } } if (contentTypeMatch) { // Now find out if it can provide tags of the type we want foreach (Type type in tagMetadata.TagTypes) { // This producer is used if it claims to produce a tag // that this type is assignable from. if (taggerType.IsAssignableFrom(type)) { return true; } } } return false; } internal class ContentAndTypeData { public readonly IContentType ContentType; public readonly Type TaggerType; public ContentAndTypeData(IContentType contentType, Type taggerType) { this.ContentType = contentType; this.TaggerType = taggerType; } public override bool Equals(object obj) { var other = obj as ContentAndTypeData; return (other != null) && (other.ContentType == this.ContentType) && (other.TaggerType == this.TaggerType); } public override int GetHashCode() { return this.ContentType.GetHashCode() ^ this.TaggerType.GetHashCode(); } } } }