From 72ec327431b87e54bae9e330e34bce84c29251aa Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Thu, 21 Dec 2017 22:42:26 -0500 Subject: [PATCH] Add MEF metadata to IDeferredQuickInfoContentToFrameworkElementConverter When I added this interface, I had the implementations expose their applicable type not by metadata, but by simply calling a method on the instance. This is problematic if the implementation is in a different assembly as we'll load it too early. This is causing F# to load when they shouldn't. This is intended as a temporary shim that fixes the perf load for the F# case (the only current exporter of these outside the Roslyn repo itself) without forcing an interface change. This will get deleted soon enough once we migrate to newer Quick Info APIs. --- .../ClassifiableDeferredContentConverter.cs | 1 + ...entationCommentDeferredContentConverter.cs | 1 + ...rojectionBufferDeferredContentConverter.cs | 1 + .../QuickInfoConverterMetadataAttribute.cs | 20 ++++++++++ ...uickInfoDisplayDeferredContentConverter.cs | 1 + .../SymbolGlyphDeferredContentConverter.cs | 1 + .../DeferredContentFrameworkElementFactory.cs | 39 ++++++++++++++++--- .../IntelliSense/QuickInfoControllerTests.vb | 4 +- 8 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoConverterMetadataAttribute.cs diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ClassifiableDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ClassifiableDeferredContentConverter.cs index 9b328b438f3..1a5c28fe5be 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ClassifiableDeferredContentConverter.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ClassifiableDeferredContentConverter.cs @@ -13,6 +13,7 @@ namespace Microsoft.CodeAnalysis.Editor.QuickInfo { [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] + [QuickInfoConverterMetadata(typeof(ClassifiableDeferredContent))] class ClassifiableDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter { private readonly ClassificationTypeMap _typeMap; diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/DocumentationCommentDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/DocumentationCommentDeferredContentConverter.cs index 81a02c18c24..2f7a6dbf844 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/DocumentationCommentDeferredContentConverter.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/DocumentationCommentDeferredContentConverter.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.QuickInfo.Converters { [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] + [QuickInfoConverterMetadata(typeof(DocumentationCommentDeferredContent))] internal sealed class DocumentationCommentDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter { private readonly ClassificationTypeMap _typeMap; diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ProjectionBufferDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ProjectionBufferDeferredContentConverter.cs index 97cb1bfb94b..70b75866815 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ProjectionBufferDeferredContentConverter.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/ProjectionBufferDeferredContentConverter.cs @@ -13,6 +13,7 @@ namespace Microsoft.CodeAnalysis.Editor.QuickInfo { [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] + [QuickInfoConverterMetadata(typeof(ProjectionBufferDeferredContent))] class ProjectionBufferDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter { private readonly IProjectionBufferFactoryService _projectionBufferFactoryService; diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoConverterMetadataAttribute.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoConverterMetadataAttribute.cs new file mode 100644 index 00000000000..1537f9bf4c7 --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoConverterMetadataAttribute.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.ComponentModel.Composition; + +namespace Microsoft.CodeAnalysis.Editor.QuickInfo +{ + [MetadataAttribute] + internal sealed class QuickInfoConverterMetadataAttribute : Attribute + { + public QuickInfoConverterMetadataAttribute(Type deferredType) + { + DeferredTypeFullName = deferredType.FullName; + } + + public string DeferredTypeFullName { get; } + } +} diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoDisplayDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoDisplayDeferredContentConverter.cs index f5eb2382cf3..68350c4da6d 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoDisplayDeferredContentConverter.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/QuickInfoDisplayDeferredContentConverter.cs @@ -10,6 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.QuickInfo { [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] + [QuickInfoConverterMetadata(typeof(QuickInfoDisplayDeferredContent))] class QuickInfoDisplayDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter { public FrameworkElement CreateFrameworkElement(IDeferredQuickInfoContent deferredContent, DeferredContentFrameworkElementFactory factory) diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/SymbolGlyphDeferredContentConverter.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/SymbolGlyphDeferredContentConverter.cs index 8da8adbafa9..cad17e81ce5 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/SymbolGlyphDeferredContentConverter.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Converters/SymbolGlyphDeferredContentConverter.cs @@ -11,6 +11,7 @@ namespace Microsoft.CodeAnalysis.Editor.QuickInfo { [Export(typeof(IDeferredQuickInfoContentToFrameworkElementConverter))] + [QuickInfoConverterMetadata(typeof(SymbolGlyphDeferredContent))] class SymbolGlyphDeferredContentConverter : IDeferredQuickInfoContentToFrameworkElementConverter { public FrameworkElement CreateFrameworkElement(IDeferredQuickInfoContent deferredContent, DeferredContentFrameworkElementFactory factory) diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/DeferredContentFrameworkElementFactory.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/DeferredContentFrameworkElementFactory.cs index a6550b8107b..2ea8f9cb830 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/DeferredContentFrameworkElementFactory.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/DeferredContentFrameworkElementFactory.cs @@ -3,24 +3,53 @@ using System.ComponentModel.Composition; using System.Linq; using System.Windows; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.QuickInfo { [Export] internal class DeferredContentFrameworkElementFactory { - private readonly Dictionary _convertersByType; + private readonly Dictionary> _convertersByTypeFullName + = new Dictionary>(); + private readonly IEnumerable> _convertersWithoutMetadata; [ImportingConstructor] - public DeferredContentFrameworkElementFactory([ImportMany] IEnumerable converters) + public DeferredContentFrameworkElementFactory( + [ImportMany] IEnumerable> converters, + [ImportMany] IEnumerable> convertersWithoutMetadata) { - _convertersByType = converters.ToDictionary(c => c.GetApplicableType()); + _convertersByTypeFullName = converters.ToDictionary( + lazy => lazy.Metadata.DeferredTypeFullName, + lazy => (Lazy)lazy); + + _convertersWithoutMetadata = convertersWithoutMetadata; } internal FrameworkElement CreateElement(IDeferredQuickInfoContent deferredContent) { - return _convertersByType[deferredContent.GetType()].CreateFrameworkElement(deferredContent, this); + var deferredContentFullName = deferredContent.GetType().FullName; + Lazy converter; + + if (!_convertersByTypeFullName.TryGetValue(deferredContentFullName, out converter)) + { + // The content must be of a type we didn't have MEF deferred metadata for. Realize the + // ones without MEF metadata, forcing everything to load. + foreach (var converterWithoutMetadata in _convertersWithoutMetadata) + { + _convertersByTypeFullName[converterWithoutMetadata.Value.GetApplicableType().FullName] = + new Lazy(() => converterWithoutMetadata.Value); + } + + Contract.ThrowIfFalse(_convertersByTypeFullName.TryGetValue(deferredContentFullName, out converter)); + } + + return converter.Value.CreateFrameworkElement(deferredContent, this); + } + + internal interface IQuickInfoConverterMetadata + { + string DeferredTypeFullName { get; } } } } diff --git a/src/EditorFeatures/Test2/IntelliSense/QuickInfoControllerTests.vb b/src/EditorFeatures/Test2/IntelliSense/QuickInfoControllerTests.vb index e78406b0f70..e79bfb5f370 100644 --- a/src/EditorFeatures/Test2/IntelliSense/QuickInfoControllerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/QuickInfoControllerTests.vb @@ -111,7 +111,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub PresenterUpdatesExistingSessionIfNotDismissed() Dim broker = New Mock(Of IQuickInfoBroker)() - Dim frameworkElementFactory = New DeferredContentFrameworkElementFactory({}) + Dim frameworkElementFactory = New DeferredContentFrameworkElementFactory({}, {}) Dim presenter As IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession) = New QuickInfoPresenter(broker.Object, frameworkElementFactory) Dim mockEditorSession = New Mock(Of IQuickInfoSession) mockEditorSession.Setup(Function(m) m.IsDismissed).Returns(False) @@ -130,7 +130,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense brokerSession.Setup(Function(m) m.Properties).Returns(New PropertyCollection()) broker.Setup(Function(m) m.CreateQuickInfoSession(It.IsAny(Of ITextView), It.IsAny(Of ITrackingPoint), It.IsAny(Of Boolean))).Returns(brokerSession.Object) - Dim frameworkElementFactory = New DeferredContentFrameworkElementFactory({}) + Dim frameworkElementFactory = New DeferredContentFrameworkElementFactory({}, {}) Dim presenter As IIntelliSensePresenter(Of IQuickInfoPresenterSession, IQuickInfoSession) = New QuickInfoPresenter(broker.Object, frameworkElementFactory) Dim mockEditorSession = New Mock(Of IQuickInfoSession) mockEditorSession.Setup(Function(m) m.IsDismissed).Returns(True) -- GitLab