File: ExtractMethod\SelectionInfo.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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 Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.ExtractMethod;
 
internal abstract partial class AbstractExtractMethodService<
    TStatementSyntax,
    TExecutableStatementSyntax,
    TExpressionSyntax>
{
    /// <summary>
    /// Information about the initial selection the user made, prior to do any fixup on it to select a more appropriate
    /// range.  Kept separate from <see cref="FinalSelectionInfo"/> to ensure different phases of the extract method
    /// process do not accidentally use the wrong information.
    /// </summary>
    internal sealed class InitialSelectionInfo
    {
        public readonly OperationStatus Status;
 
        public readonly bool SelectionInExpression;
 
        public readonly SyntaxToken FirstTokenInOriginalSpan;
        public readonly SyntaxToken LastTokenInOriginalSpan;
 
        public readonly TStatementSyntax? FirstStatement;
        public readonly TStatementSyntax? LastStatement;
 
        private InitialSelectionInfo(OperationStatus status)
            => Status = status;
 
        public InitialSelectionInfo(
            OperationStatus status,
            SyntaxToken firstTokenInOriginalSpan,
            SyntaxToken lastTokenInOriginalSpan,
            TStatementSyntax? firstStatement,
            TStatementSyntax? lastStatement,
            bool selectionInExpression)
            : this(status)
        {
            FirstTokenInOriginalSpan = firstTokenInOriginalSpan;
            LastTokenInOriginalSpan = lastTokenInOriginalSpan;
            FirstStatement = firstStatement;
            LastStatement = lastStatement;
            SelectionInExpression = selectionInExpression;
        }
 
        public static InitialSelectionInfo Failure(string reason)
            => new(new(succeeded: false, reason));
 
        public SyntaxNode CommonRoot => this.FirstTokenInOriginalSpan.GetCommonRoot(this.LastTokenInOriginalSpan);
    }
 
    /// <summary>
    /// Information about the final selection we adjust the the user's selection to. Kept separate from <see
    /// cref="InitialSelectionInfo"/> to ensure different phases of the extract method process do not accidentally use
    /// the wrong information.
    /// </summary>
    internal sealed record FinalSelectionInfo
    {
        public required OperationStatus Status { get; init; }
 
        public TextSpan FinalSpan { get; init; }
 
        public SyntaxToken FirstTokenInFinalSpan { get; init; }
        public SyntaxToken LastTokenInFinalSpan { get; init; }
 
        public bool SelectionInExpression { get; init; }
 
        /// <summary>
        /// For VB.  C# should just use standard <c>with</c> operator.
        /// </summary>
        public FinalSelectionInfo With(
            Optional<OperationStatus> status = default,
            Optional<TextSpan> finalSpan = default,
            Optional<SyntaxToken> firstTokenInFinalSpan = default,
            Optional<SyntaxToken> lastTokenInFinalSpan = default,
            Optional<bool> selectionInExpression = default)
        {
            var resultStatus = status.HasValue ? status.Value : this.Status;
            var resultFinalSpan = finalSpan.HasValue ? finalSpan.Value : this.FinalSpan;
            var resultFirstTokenInFinalSpan = firstTokenInFinalSpan.HasValue ? firstTokenInFinalSpan.Value : this.FirstTokenInFinalSpan;
            var resultLastTokenInFinalSpan = lastTokenInFinalSpan.HasValue ? lastTokenInFinalSpan.Value : this.LastTokenInFinalSpan;
            var resultSelectionInExpression = selectionInExpression.HasValue ? selectionInExpression.Value : this.SelectionInExpression;
 
            return this with
            {
                Status = resultStatus,
                FinalSpan = resultFinalSpan,
                FirstTokenInFinalSpan = resultFirstTokenInFinalSpan,
                LastTokenInFinalSpan = resultLastTokenInFinalSpan,
                SelectionInExpression = resultSelectionInExpression,
            };
        }
 
        public SelectionType GetSelectionType()
        {
            if (this.SelectionInExpression)
                return SelectionType.Expression;
 
            var firstStatement = this.FirstTokenInFinalSpan.GetRequiredAncestor<TExecutableStatementSyntax>();
            var lastStatement = this.LastTokenInFinalSpan.GetRequiredAncestor<TExecutableStatementSyntax>();
            if (firstStatement.Span.Contains(lastStatement.Span))
                return SelectionType.SingleStatement;
 
            return SelectionType.MultipleStatements;
        }
    }
}