diff --git a/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionProvider.cs b/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionProvider.cs index 8b04eebdce..66505c2115 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionProvider.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/RazorDirectiveCompletionProvider.cs @@ -37,15 +37,13 @@ namespace Microsoft.VisualStudio.Editor.Razor CSharpCodeParser.TagHelperPrefixDirectiveDescriptor, }; private readonly Lazy _codeDocumentProvider; - private readonly Lazy _completionFactsService; - private readonly IAsyncCompletionBroker _asyncCompletionBroker; + private readonly Lazy _dependencies; private readonly RazorTextBufferProvider _textBufferProvider; [ImportingConstructor] public RazorDirectiveCompletionProvider( [Import(typeof(RazorCodeDocumentProvider))] Lazy codeDocumentProvider, - [Import(typeof(RazorCompletionFactsService))] Lazy completionFactsService, - IAsyncCompletionBroker asyncCompletionBroker, + [Import(typeof(CompletionProviderDependencies))] Lazy dependencies, RazorTextBufferProvider textBufferProvider) { if (codeDocumentProvider == null) @@ -53,14 +51,9 @@ namespace Microsoft.VisualStudio.Editor.Razor throw new ArgumentNullException(nameof(codeDocumentProvider)); } - if (completionFactsService == null) + if (dependencies == null) { - throw new ArgumentNullException(nameof(completionFactsService)); - } - - if (asyncCompletionBroker == null) - { - throw new ArgumentNullException(nameof(asyncCompletionBroker)); + throw new ArgumentNullException(nameof(dependencies)); } if (textBufferProvider == null) @@ -69,8 +62,7 @@ namespace Microsoft.VisualStudio.Editor.Razor } _codeDocumentProvider = codeDocumentProvider; - _completionFactsService = completionFactsService; - _asyncCompletionBroker = asyncCompletionBroker; + _dependencies = dependencies; _textBufferProvider = textBufferProvider; } @@ -112,13 +104,6 @@ namespace Microsoft.VisualStudio.Editor.Razor return Task.CompletedTask; } - if (!_textBufferProvider.TryGetFromDocument(context.Document, out var textBuffer) || - !_asyncCompletionBroker.IsCompletionSupported(textBuffer.ContentType)) - { - // Completion is not supported. - return Task.CompletedTask; - } - var result = AddCompletionItems(context); return result; @@ -129,6 +114,13 @@ namespace Microsoft.VisualStudio.Editor.Razor [MethodImpl(MethodImplOptions.NoInlining)] private Task AddCompletionItems(CompletionContext context) { + if (!_textBufferProvider.TryGetFromDocument(context.Document, out var textBuffer) || + !_dependencies.Value.AsyncCompletionBroker.IsCompletionSupported(textBuffer.ContentType)) + { + // Completion is not supported. + return Task.CompletedTask; + } + if (!_codeDocumentProvider.Value.TryGetFromDocument(context.Document, out var codeDocument)) { // A Razor code document has not yet been associated with the document. @@ -149,7 +141,7 @@ namespace Microsoft.VisualStudio.Editor.Razor } var location = new SourceSpan(razorSnapshotPoint.Position, 0); - var razorCompletionItems = _completionFactsService.Value.GetCompletionItems(syntaxTree, location); + var razorCompletionItems = _dependencies.Value.CompletionFactsService.GetCompletionItems(syntaxTree, location); foreach (var razorCompletionItem in razorCompletionItems) { @@ -208,4 +200,40 @@ namespace Microsoft.VisualStudio.Editor.Razor return false; } } + + // These types are only for this class to provide indirection for assembly loads. + internal abstract class CompletionProviderDependencies + { + public abstract RazorCompletionFactsService CompletionFactsService { get; } + + public abstract IAsyncCompletionBroker AsyncCompletionBroker { get; } + } + + [System.Composition.Shared] + [Export(typeof(CompletionProviderDependencies))] + internal class DefaultCompletionProviderDependencies : CompletionProviderDependencies + { + [ImportingConstructor] + public DefaultCompletionProviderDependencies( + RazorCompletionFactsService completionFactsService, + IAsyncCompletionBroker asyncCompletionBroker) + { + if (completionFactsService == null) + { + throw new ArgumentNullException(nameof(completionFactsService)); + } + + if (asyncCompletionBroker == null) + { + throw new ArgumentNullException(nameof(asyncCompletionBroker)); + } + + CompletionFactsService = completionFactsService; + AsyncCompletionBroker = asyncCompletionBroker; + } + + public override RazorCompletionFactsService CompletionFactsService { get; } + + public override IAsyncCompletionBroker AsyncCompletionBroker { get; } + } } diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorDirectiveCompletionProviderTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorDirectiveCompletionProviderTest.cs index 7180a72937..7fccf44a9d 100644 --- a/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorDirectiveCompletionProviderTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/RazorDirectiveCompletionProviderTest.cs @@ -30,8 +30,11 @@ namespace Microsoft.VisualStudio.Editor.Razor var razorBuffer = Mock.Of(buffer => buffer.ContentType == Mock.Of()); TextBufferProvider = Mock.Of(provider => provider.TryGetFromDocument(It.IsAny(), out razorBuffer) == true); CompletionFactsService = new DefaultRazorCompletionFactsService(); + CompletionProviderDependencies = new Lazy(() => new DefaultCompletionProviderDependencies(CompletionFactsService, CompletionBroker)); } + private Lazy CompletionProviderDependencies { get; } + private IAsyncCompletionBroker CompletionBroker { get; } private RazorTextBufferProvider TextBufferProvider { get; } @@ -52,8 +55,7 @@ namespace Microsoft.VisualStudio.Editor.Razor var codeDocumentProvider = new Mock(); var completionProvider = new RazorDirectiveCompletionProvider( new Lazy(() => codeDocumentProvider.Object), - new Lazy(() => CompletionFactsService), - CompletionBroker, + CompletionProviderDependencies, TextBufferProvider); // Act @@ -75,8 +77,7 @@ namespace Microsoft.VisualStudio.Editor.Razor var codeDocumentProvider = new Mock(); var completionProvider = new RazorDirectiveCompletionProvider( new Lazy(() => codeDocumentProvider.Object), - new Lazy(() => CompletionFactsService), - CompletionBroker, + CompletionProviderDependencies, TextBufferProvider); // Act @@ -94,7 +95,7 @@ namespace Microsoft.VisualStudio.Editor.Razor var codeDocumentProvider = new Mock(MockBehavior.Strict); var completionProvider = new FailOnGetCompletionsProvider( new Lazy(() => codeDocumentProvider.Object), - CompletionBroker, + CompletionProviderDependencies, TextBufferProvider); var document = CreateDocument(); document = document.WithFilePath("NotRazor.cs"); @@ -122,7 +123,7 @@ namespace Microsoft.VisualStudio.Editor.Razor var codeDocumentProvider = new Mock(MockBehavior.Strict); var completionProvider = new FailOnGetCompletionsProvider( new Lazy(() => codeDocumentProvider.Object), - CompletionBroker, + CompletionProviderDependencies, TextBufferProvider); var context = CreateContext(1, completionProvider, document); @@ -140,7 +141,7 @@ namespace Microsoft.VisualStudio.Editor.Razor .Returns(false); var completionProvider = new FailOnGetCompletionsProvider( new Lazy(() => codeDocumentProvider.Object), - CompletionBroker, + CompletionProviderDependencies, TextBufferProvider); var document = CreateDocument(); var context = CreateContext(1, completionProvider, document); @@ -156,7 +157,7 @@ namespace Microsoft.VisualStudio.Editor.Razor var codeDocumentProvider = CreateCodeDocumentProvider("@", Enumerable.Empty()); var completionProvider = new FailOnGetCompletionsProvider( codeDocumentProvider, - CompletionBroker, + CompletionProviderDependencies, TextBufferProvider, canGetSnapshotPoint: false); var document = CreateDocument(); @@ -223,10 +224,10 @@ namespace Microsoft.VisualStudio.Editor.Razor public FailOnGetCompletionsProvider( Lazy codeDocumentProvider, - IAsyncCompletionBroker asyncCompletionBroker, + Lazy completionProviderDependencies, RazorTextBufferProvider textBufferProvider, bool canGetSnapshotPoint = true) - : base(codeDocumentProvider, new Lazy(() => new DefaultRazorCompletionFactsService()), asyncCompletionBroker, textBufferProvider) + : base(codeDocumentProvider, completionProviderDependencies, textBufferProvider) { _canGetSnapshotPoint = canGetSnapshotPoint; }