File: AbstractTriviaDataFactory.cs
Web Access
Project: ..\..\..\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.Threading;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Formatting
{
    internal abstract partial class AbstractTriviaDataFactory
    {
        private const int SpaceCacheSize = 10;
        private const int LineBreakCacheSize = 5;
        private const int IndentationLevelCacheSize = 20;
 
        protected readonly TreeData TreeInfo;
        protected readonly SyntaxFormattingOptions Options;
 
        private readonly Whitespace[] _spaces;
        private readonly Whitespace?[,] _whitespaces = new Whitespace[LineBreakCacheSize, IndentationLevelCacheSize];
 
        protected AbstractTriviaDataFactory(TreeData treeInfo, SyntaxFormattingOptions options)
        {
            Contract.ThrowIfNull(treeInfo);
 
            TreeInfo = treeInfo;
            Options = options;
 
            _spaces = new Whitespace[SpaceCacheSize];
            for (var i = 0; i < SpaceCacheSize; i++)
            {
                _spaces[i] = new Whitespace(Options, space: i, elastic: false, language: treeInfo.Root.Language);
            }
        }
 
        protected TriviaData GetSpaceTriviaData(int space, bool elastic = false)
        {
            Contract.ThrowIfFalse(space >= 0);
 
            // if result has elastic trivia in them, never use cache
            if (elastic)
            {
                return new Whitespace(this.Options, space, elastic: true, language: this.TreeInfo.Root.Language);
            }
 
            if (space < SpaceCacheSize)
            {
                return _spaces[space];
            }
 
            // create a new space
            return new Whitespace(this.Options, space, elastic: false, language: this.TreeInfo.Root.Language);
        }
 
        protected TriviaData GetWhitespaceTriviaData(int lineBreaks, int indentation, bool useTriviaAsItIs, bool elastic)
        {
            Contract.ThrowIfFalse(lineBreaks >= 0);
            Contract.ThrowIfFalse(indentation >= 0);
 
            // we can use cache
            //  #1. if whitespace trivia don't have any elastic trivia and
            //  #2. analysis (Item1) didn't find anything preventing us from using cache such as trailing whitespace before new line
            //  #3. number of line breaks (Item2) are under cache capacity (line breaks)
            //  #4. indentation (Item3) is aligned to indentation level
            var canUseCache = !elastic &&
                              useTriviaAsItIs &&
                              lineBreaks > 0 &&
                              lineBreaks <= LineBreakCacheSize &&
                              indentation % Options.IndentationSize == 0;
 
            if (canUseCache)
            {
                var indentationLevel = indentation / Options.IndentationSize;
                if (indentationLevel < IndentationLevelCacheSize)
                {
                    var lineIndex = lineBreaks - 1;
                    EnsureWhitespaceTriviaInfo(lineIndex, indentationLevel);
                    return _whitespaces[lineIndex, indentationLevel]!;
                }
            }
 
            return
                useTriviaAsItIs
                    ? new Whitespace(this.Options, lineBreaks, indentation, elastic, language: this.TreeInfo.Root.Language)
                    : new ModifiedWhitespace(this.Options, lineBreaks, indentation, elastic, language: this.TreeInfo.Root.Language);
        }
 
        private void EnsureWhitespaceTriviaInfo(int lineIndex, int indentationLevel)
        {
            Contract.ThrowIfFalse(lineIndex is >= 0 and < LineBreakCacheSize);
            Contract.ThrowIfFalse(indentationLevel >= 0 && indentationLevel < _whitespaces.Length / _whitespaces.Rank);
 
            // set up caches
            if (_whitespaces[lineIndex, indentationLevel] == null)
            {
                var indentation = indentationLevel * Options.IndentationSize;
                var triviaInfo = new Whitespace(Options, lineBreaks: lineIndex + 1, indentation: indentation, elastic: false, language: this.TreeInfo.Root.Language);
                Interlocked.CompareExchange(ref _whitespaces[lineIndex, indentationLevel], triviaInfo, null);
            }
        }
 
        public abstract TriviaData CreateLeadingTrivia(SyntaxToken token);
        public abstract TriviaData CreateTrailingTrivia(SyntaxToken token);
        public abstract TriviaData Create(SyntaxToken token1, SyntaxToken token2);
    }
}