File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\Formatting\Engine\AbstractAggregatedFormattingResult.cs
Web Access
Project: src\src\CodeStyle\Core\Analyzers\Microsoft.CodeAnalysis.CodeStyle.csproj (Microsoft.CodeAnalysis.CodeStyle)
// 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.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Formatting;
 
internal abstract class AbstractAggregatedFormattingResult : IFormattingResult
{
    protected readonly SyntaxNode Node;
 
    private readonly IList<AbstractFormattingResult> _formattingResults;
    private readonly TextSpanMutableIntervalTree? _formattingSpans;
 
    private readonly CancellableLazy<IList<TextChange>> _lazyTextChanges;
    private readonly CancellableLazy<SyntaxNode> _lazyNode;
 
    public AbstractAggregatedFormattingResult(
        SyntaxNode node,
        IList<AbstractFormattingResult> formattingResults,
        TextSpanMutableIntervalTree? formattingSpans)
    {
        Contract.ThrowIfNull(node);
        Contract.ThrowIfNull(formattingResults);
 
        this.Node = node;
        _formattingResults = formattingResults;
        _formattingSpans = formattingSpans;
 
        _lazyTextChanges = new CancellableLazy<IList<TextChange>>(CreateTextChanges);
        _lazyNode = new CancellableLazy<SyntaxNode>(CreateFormattedRoot);
    }
 
    /// <summary>
    /// rewrite the node with the given trivia information in the map
    /// </summary>
    protected abstract SyntaxNode Rewriter(Dictionary<ValueTuple<SyntaxToken, SyntaxToken>, TriviaData> changeMap, CancellationToken cancellationToken);
 
    protected TextSpanMutableIntervalTree GetFormattingSpans()
        => _formattingSpans ?? new TextSpanMutableIntervalTree(_formattingResults.Select(r => r.FormattedSpan));
 
    #region IFormattingResult implementation
 
    public bool ContainsChanges
    {
        get
        {
            return this.GetTextChanges(CancellationToken.None).Count > 0;
        }
    }
 
    public IList<TextChange> GetTextChanges(CancellationToken cancellationToken)
        => _lazyTextChanges.GetValue(cancellationToken);
 
    public SyntaxNode GetFormattedRoot(CancellationToken cancellationToken)
        => _lazyNode.GetValue(cancellationToken);
 
    private IList<TextChange> CreateTextChanges(CancellationToken cancellationToken)
    {
        using (Logger.LogBlock(FunctionId.Formatting_AggregateCreateTextChanges, cancellationToken))
        {
            // quick check
            var changes = CreateTextChangesWorker(cancellationToken);
 
            // formatted spans and formatting spans are different, filter returns to formatting span
            return _formattingSpans == null
                ? changes
                : changes.Where(s => _formattingSpans.HasIntervalThatIntersectsWith(s.Span)).ToList();
        }
    }
 
    private IList<TextChange> CreateTextChangesWorker(CancellationToken cancellationToken)
    {
        if (_formattingResults.Count == 1)
        {
            return _formattingResults[0].GetTextChanges(cancellationToken);
        }
 
        // pre-allocate list
        var count = _formattingResults.Sum(r => r.GetTextChanges(cancellationToken).Count);
        var result = new List<TextChange>(count);
        foreach (var formattingResult in _formattingResults)
        {
            result.AddRange(formattingResult.GetTextChanges(cancellationToken));
        }
 
        return result;
    }
 
    private SyntaxNode CreateFormattedRoot(CancellationToken cancellationToken)
    {
        using (Logger.LogBlock(FunctionId.Formatting_AggregateCreateFormattedRoot, cancellationToken))
        {
            // create a map
            var map = new Dictionary<ValueTuple<SyntaxToken, SyntaxToken>, TriviaData>();
 
            _formattingResults.Do(result => result.GetChanges(cancellationToken).Do(change => map.Add(change.Item1, change.Item2)));
 
            return Rewriter(map, cancellationToken);
        }
    }
 
    #endregion
}