File: CodeRefactorings\CodeRefactoringContext.cs
Web Access
Project: src\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;
using System.ComponentModel;
using System.Threading;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.CodeRefactorings;
 
/// <summary>
/// Context for code refactorings provided by a <see cref="CodeRefactoringProvider"/>.
/// </summary>
public readonly struct CodeRefactoringContext
{
    /// <summary>
    /// Document corresponding to the <see cref="CodeRefactoringContext.Span"/> to refactor.
    /// For code refactorings that support non-source documents by providing a non-default value for
    /// <see cref="ExportCodeRefactoringProviderAttribute.DocumentKinds"/>, this property will
    /// throw an <see cref="InvalidOperationException"/>. Such refactorings should use the
    /// <see cref="CodeRefactoringContext.TextDocument"/> property instead.
    /// </summary>
    public Document Document
    {
        get
        {
            if (TextDocument is not Document document)
            {
                throw new InvalidOperationException(WorkspacesResources.Use_TextDocument_property_instead_of_Document_property_as_the_provider_supports_non_source_text_documents);
            }
 
            return document;
        }
    }
 
    /// <summary>
    /// TextDocument corresponding to the <see cref="CodeRefactoringContext.Span"/> to refactor.
    /// This property should be used instead of <see cref="CodeRefactoringContext.Document"/> property by
    /// code refactorings that support non-source documents by providing a non-default value for
    /// <see cref="ExportCodeRefactoringProviderAttribute.DocumentKinds"/>
    /// </summary>
    public TextDocument TextDocument { get; }
 
    /// <summary>
    /// Text span within the <see cref="CodeRefactoringContext.Document"/> or <see cref="CodeRefactoringContext.TextDocument"/> to refactor.
    /// </summary>
    public TextSpan Span { get; }
 
    /// <summary>
    /// CancellationToken.
    /// </summary>
    public CancellationToken CancellationToken { get; }
 
    private readonly Action<CodeAction, TextSpan?> _registerRefactoring;
 
    /// <summary>
    /// Creates a code refactoring context to be passed into <see cref="CodeRefactoringProvider.ComputeRefactoringsAsync(CodeRefactoringContext)"/> method.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public CodeRefactoringContext(
        Document document,
        TextSpan span,
        Action<CodeAction> registerRefactoring,
        CancellationToken cancellationToken)
        : this(document, span, (action, textSpan) => registerRefactoring(action), cancellationToken)
    { }
 
    /// <summary>
    /// Creates a code refactoring context to be passed into <see cref="CodeRefactoringProvider.ComputeRefactoringsAsync(CodeRefactoringContext)"/> method.
    /// </summary>
    public CodeRefactoringContext(
        TextDocument document,
        TextSpan span,
        Action<CodeAction> registerRefactoring,
        CancellationToken cancellationToken)
        : this(document, span, (action, textSpan) => registerRefactoring(action), cancellationToken)
    { }
 
    /// <summary>
    /// Creates a code refactoring context to be passed into <see cref="CodeRefactoringProvider.ComputeRefactoringsAsync(CodeRefactoringContext)"/> method.
    /// </summary>
    internal CodeRefactoringContext(
        TextDocument document,
        TextSpan span,
        Action<CodeAction, TextSpan?> registerRefactoring,
        CancellationToken cancellationToken)
    {
        // NOTE/TODO: Don't make this overload public & obsolete the `Action<CodeAction> registerRefactoring`
        // overload to stop leaking the Lambda implementation detail.
        TextDocument = document ?? throw new ArgumentNullException(nameof(document));
        Span = span;
        _registerRefactoring = registerRefactoring ?? throw new ArgumentNullException(nameof(registerRefactoring));
        CancellationToken = cancellationToken;
    }
 
    /// <summary>
    /// Add supplied <paramref name="action"/> to the list of refactorings that will be offered to the user.
    /// </summary>
    /// <param name="action">The <see cref="CodeAction"/> that will be invoked to apply the refactoring.</param>
    public void RegisterRefactoring(CodeAction action) => RegisterRefactoring(action, applicableToSpan: null); // We could pass this.Span as applicableToSpan instead but that would cause these refactorings to always be closest to current selection
 
    /// <summary>
    /// Add supplied <paramref name="action"/> applicable to <paramref name="applicableToSpan"/> to the list of refactorings that will be offered to the user.
    /// </summary>
    /// <param name="action">The <see cref="CodeAction"/> that will be invoked to apply the refactoring.</param>
    /// <param name="applicableToSpan">The <see cref="TextSpan"/> within original document the <paramref name="action"/> is applicable to.</param>
    /// <remarks>
    /// <paramref name="applicableToSpan"/> should represent a logical section within the original document that the <paramref name="action"/> is 
    /// applicable to. It doesn't have to precisely represent the exact <see cref="TextSpan"/> that will get changed.
    /// </remarks>
    internal void RegisterRefactoring(CodeAction action, TextSpan applicableToSpan) => RegisterRefactoring(action, new Nullable<TextSpan>(applicableToSpan));
 
    private void RegisterRefactoring(CodeAction action, TextSpan? applicableToSpan)
    {
        if (action == null)
        {
            throw new ArgumentNullException(nameof(action));
        }
 
        _registerRefactoring(action, applicableToSpan);
    }
 
    internal void Deconstruct(out Document document, out TextSpan span, out CancellationToken cancellationToken)
    {
        document = Document;
        span = Span;
        cancellationToken = CancellationToken;
    }
}