88 lines
3.2 KiB
C#
88 lines
3.2 KiB
C#
/*
|
|
Copyright (C) 2014-2019 de4dot@gmail.com
|
|
|
|
This file is part of dnSpy
|
|
|
|
dnSpy is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
dnSpy is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using Microsoft.VisualStudio.Utilities;
|
|
|
|
namespace dnSpy.Text {
|
|
sealed class ProviderSelector<TProvider, TProviderMetadata> where TProviderMetadata : IContentTypeMetadata {
|
|
readonly IContentTypeRegistryService contentTypeRegistryService;
|
|
readonly Dictionary<IContentType, Lazy<TProvider, TProviderMetadata>[]> dict;
|
|
readonly Lazy<TProvider, TProviderMetadata>[] providers;
|
|
|
|
public ProviderSelector(IContentTypeRegistryService contentTypeRegistryService, IEnumerable<Lazy<TProvider, TProviderMetadata>> providers) {
|
|
this.contentTypeRegistryService = contentTypeRegistryService ?? throw new ArgumentNullException(nameof(contentTypeRegistryService));
|
|
dict = new Dictionary<IContentType, Lazy<TProvider, TProviderMetadata>[]>();
|
|
this.providers = providers.ToArray();
|
|
}
|
|
|
|
public IEnumerable<Lazy<TProvider, TProviderMetadata>> GetProviders(IContentType contentType) {
|
|
if (contentType is null)
|
|
throw new ArgumentNullException(nameof(contentType));
|
|
|
|
if (!dict.TryGetValue(contentType, out var result))
|
|
dict[contentType] = result = CreateProviderList(contentType);
|
|
return result;
|
|
}
|
|
|
|
Lazy<TProvider, TProviderMetadata>[] CreateProviderList(IContentType contentType) {
|
|
List<(Lazy<TProvider, TProviderMetadata> lz, int dist)>? list = null;
|
|
|
|
// We only allow a provider to match if its supported content type equals the
|
|
// requested content type or if it's a child of the requested content type.
|
|
// Eg. "CSharp" only matches "CSharp" (since nothing derives from it), so the
|
|
// Roslyn text structure navigator provider won't match "text" content types.
|
|
|
|
foreach (var provider in providers) {
|
|
foreach (var ctString in provider.Metadata.ContentTypes) {
|
|
var ct = contentTypeRegistryService.GetContentType(ctString);
|
|
Debug2.Assert(ct is not null);
|
|
if (ct is null)
|
|
continue;
|
|
int dist = GetDistance(ct, contentType);
|
|
if (dist < 0)
|
|
continue;
|
|
if (list is null)
|
|
list = new List<(Lazy<TProvider, TProviderMetadata>, int)>();
|
|
list.Add((provider, dist));
|
|
}
|
|
}
|
|
|
|
if (list is null)
|
|
return Array.Empty<Lazy<TProvider, TProviderMetadata>>();
|
|
list.Sort((a, b) => a.dist - b.dist);
|
|
return list.Select(a => a.lz).ToArray();
|
|
}
|
|
|
|
int GetDistance(IContentType baseType, IContentType other) {
|
|
if (baseType == other)
|
|
return 0;
|
|
foreach (var bt in other.BaseTypes) {
|
|
int dist = GetDistance(baseType, bt);
|
|
if (dist >= 0)
|
|
return dist + 1;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
}
|