﻿// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.ExternalAccess.Razor
{
    internal sealed class RazorSpanMappingServiceWrapper : ISpanMappingService
    {
        private readonly IRazorSpanMappingService _razorSpanMappingService;

        public RazorSpanMappingServiceWrapper(IRazorSpanMappingService razorSpanMappingService)
        {
            _razorSpanMappingService = razorSpanMappingService ?? throw new ArgumentNullException(nameof(razorSpanMappingService));
        }

        public async Task<ImmutableArray<MappedSpanResult>> MapSpansAsync(Document document, IEnumerable<TextSpan> spans, CancellationToken cancellationToken)
        {
            var razorSpans = await _razorSpanMappingService.MapSpansAsync(document, spans, cancellationToken).ConfigureAwait(false);
            var roslynSpans = new MappedSpanResult[razorSpans.Length];
            for (var i = 0; i < razorSpans.Length; i++)
            {
                var razorSpan = razorSpans[i];
                roslynSpans[i] = new MappedSpanResult(razorSpan.FilePath, razorSpan.LinePositionSpan, razorSpan.Span);
            }

            return roslynSpans.ToImmutableArray();
        }
    }
}

﻿// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.ExternalAccess.Razor
{
    internal sealed class RazorSpanMappingServiceWrapper : ISpanMappingService
    {
        private readonly IRazorSpanMappingService _razorSpanMappingService;

        public RazorSpanMappingServiceWrapper(IRazorSpanMappingService razorSpanMappingService)
        {
            _razorSpanMappingService = razorSpanMappingService ?? throw new ArgumentNullException(nameof(razorSpanMappingService));
        }

        public async Task<ImmutableArray<MappedSpanResult>> MapSpansAsync(Document document, IEnumerable<TextSpan> spans, CancellationToken cancellationToken)
        {
            var razorSpans = await _razorSpanMappingService.MapSpansAsync(document, spans, cancellationToken).ConfigureAwait(false);
            var roslynSpans = new MappedSpanResult[razorSpans.Length];
            for (var i = 0; i < razorSpans.Length; i++)
            {
                var razorSpan = razorSpans[i];
                if (razorSpan.FilePath == null)
                {
                    // Unmapped location
                    roslynSpans[i] = default;
                }
                else
                {
                    roslynSpans[i] = new MappedSpanResult(razorSpan.FilePath, razorSpan.LinePositionSpan, razorSpan.Span);
                }
            }

            return roslynSpans.ToImmutableArray();
        }
    }
}

Enable default mapped span results for Razor.
Enable default mapped span results for Razor.

- Default mapped span results are used to indicate that a dynamic file could not map a particular location. Prior to this change we'd always reconstruct the span mapping full-sale which would explode during mapping construction because null filepaths weren't allowed. Therefore, we needed to understand that an "unmapped" location was being handed off from Razor's runtime and in turn needed to generate a "default" unmapped span for it.

Fixes dotnet/aspnetcore#24351
