File: RazorDocumentServiceProviderWrapper.cs
Web Access
Project: src\src\Tools\ExternalAccess\Razor\Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj (Microsoft.CodeAnalysis.ExternalAccess.Razor)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.ExternalAccess.Razor
{
    internal sealed class RazorDocumentServiceProviderWrapper : IDocumentServiceProvider, IDocumentOperationService
    {
        private readonly IRazorDocumentServiceProvider _innerDocumentServiceProvider;
 
        // The lazily initialized service fields use StrongBox<T> to explicitly allow null as an initialized value.
        private StrongBox<ISpanMappingService?>? _lazySpanMappingService;
        private StrongBox<IDocumentExcerptService?>? _lazyExcerptService;
        private StrongBox<DocumentPropertiesService?>? _lazyDocumentPropertiesService;
 
        public RazorDocumentServiceProviderWrapper(IRazorDocumentServiceProvider innerDocumentServiceProvider)
        {
            _innerDocumentServiceProvider = innerDocumentServiceProvider ?? throw new ArgumentNullException(nameof(innerDocumentServiceProvider));
        }
 
        public bool CanApplyChange => _innerDocumentServiceProvider.CanApplyChange;
 
        public bool SupportDiagnostics => _innerDocumentServiceProvider.SupportDiagnostics;
 
        public TService? GetService<TService>() where TService : class, IDocumentService
        {
            var serviceType = typeof(TService);
            if (serviceType == typeof(ISpanMappingService))
            {
                var spanMappingService = InterlockedOperations.Initialize(
                    ref _lazySpanMappingService,
                    static documentServiceProvider =>
                    {
                        // Razor is transitioning implementations from IRazorSpanMappingService to IRazorMappingService.
                        // While this is happening the service may not be available. If it is, use the newer implementation,
                        // otherwise fallback to IRazorSpanMappingService
                        var razorMappingService = documentServiceProvider.GetService<IRazorMappingService>();
                        if (razorMappingService is not null)
                        {
                            return new RazorMappingServiceWrapper(razorMappingService);
                        }
 
                        var razorSpanMappingService = documentServiceProvider.GetService<IRazorSpanMappingService>();
                        return razorSpanMappingService != null ? new RazorSpanMappingServiceWrapper(razorSpanMappingService) : null;
                    },
                    _innerDocumentServiceProvider);
 
                return (TService?)spanMappingService;
            }
 
            if (serviceType == typeof(IDocumentExcerptService))
            {
                var excerptService = InterlockedOperations.Initialize(
                    ref _lazyExcerptService,
                    static documentServiceProvider =>
                    {
                        var impl = documentServiceProvider.GetService<IRazorDocumentExcerptServiceImplementation>();
                        return (impl != null) ? new RazorDocumentExcerptServiceWrapper(impl) : null;
                    },
                    _innerDocumentServiceProvider);
 
                return (TService?)excerptService;
            }
 
            if (serviceType == typeof(DocumentPropertiesService))
            {
                var documentPropertiesService = InterlockedOperations.Initialize(
                    ref _lazyDocumentPropertiesService,
                    static documentServiceProvider =>
                    {
                        var razorDocumentPropertiesService = documentServiceProvider.GetService<IRazorDocumentPropertiesService>();
                        return razorDocumentPropertiesService is not null ? new RazorDocumentPropertiesServiceWrapper(razorDocumentPropertiesService) : null;
                    },
                    _innerDocumentServiceProvider);
 
                return (TService?)(object?)documentPropertiesService;
            }
 
            return this as TService;
        }
    }
}