//
// 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();
}
}
}
}