// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CodeFixes
{
///
/// Context for code fixes provided by a .
///
public struct CodeFixContext
{
private readonly Document _document;
private readonly Project _project;
private readonly TextSpan _span;
private readonly ImmutableArray _diagnostics;
private readonly CancellationToken _cancellationToken;
private readonly Action> _registerCodeFix;
///
/// Document corresponding to the to fix.
///
public Document Document => _document;
///
/// Project corresponding to the diagnostics to fix.
///
internal Project Project => _project;
///
/// Text span within the to fix.
///
public TextSpan Span => _span;
///
/// Diagnostics to fix.
/// NOTE: All the diagnostics in this collection have the same .
///
public ImmutableArray Diagnostics => _diagnostics;
///
/// CancellationToken.
///
public CancellationToken CancellationToken => _cancellationToken;
///
/// Creates a code fix context to be passed into method.
///
/// Document to fix.
/// Text span within the to fix.
///
/// Diagnostics to fix.
/// All the diagnostics must have the same .
/// Additionally, the of each diagnostic must be in the set of the of the associated .
///
/// Delegate to register a fixing a subset of diagnostics.
/// Cancellation token.
/// Throws this exception if any of the arguments is null.
///
/// Throws this exception if the given is empty,
/// has a null element or has an element whose span is not equal to .
///
public CodeFixContext(
Document document,
TextSpan span,
ImmutableArray diagnostics,
Action> registerCodeFix,
CancellationToken cancellationToken)
: this(document, span, diagnostics, registerCodeFix, verifyArguments: true, cancellationToken: cancellationToken)
{
}
///
/// Creates a code fix context to be passed into method.
///
/// Document to fix.
///
/// Diagnostic to fix.
/// The of this diagnostic must be in the set of the of the associated .
///
/// Delegate to register a fixing a subset of diagnostics.
/// Cancellation token.
/// Throws this exception if any of the arguments is null.
public CodeFixContext(
Document document,
Diagnostic diagnostic,
Action> registerCodeFix,
CancellationToken cancellationToken)
: this(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), registerCodeFix, verifyArguments: true, cancellationToken: cancellationToken)
{
}
internal CodeFixContext(
Document document,
TextSpan span,
ImmutableArray diagnostics,
Action> registerCodeFix,
bool verifyArguments,
CancellationToken cancellationToken)
: this(document, document.Project, span, diagnostics, registerCodeFix, verifyArguments, cancellationToken)
{
}
internal CodeFixContext(
Project project,
ImmutableArray diagnostics,
Action> registerCodeFix,
CancellationToken cancellationToken)
: this(document: null, project: project, span: default, diagnostics: diagnostics, registerCodeFix: registerCodeFix, verifyArguments: false, cancellationToken: cancellationToken)
{
}
private CodeFixContext(
Document document,
Project project,
TextSpan span,
ImmutableArray diagnostics,
Action> registerCodeFix,
bool verifyArguments,
CancellationToken cancellationToken)
{
if (verifyArguments)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
if (registerCodeFix == null)
{
throw new ArgumentNullException(nameof(registerCodeFix));
}
VerifyDiagnosticsArgument(diagnostics, span);
}
_document = document;
_project = project;
_span = span;
_diagnostics = diagnostics;
_registerCodeFix = registerCodeFix;
_cancellationToken = cancellationToken;
}
internal CodeFixContext(
Document document,
Diagnostic diagnostic,
Action> registerCodeFix,
bool verifyArguments,
CancellationToken cancellationToken)
: this(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), registerCodeFix, verifyArguments, cancellationToken)
{
}
///
/// Add supplied to the list of fixes that will be offered to the user.
///
/// The that will be invoked to apply the fix.
/// The subset of being addressed / fixed by the .
public void RegisterCodeFix(CodeAction action, Diagnostic diagnostic)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
if (diagnostic == null)
{
throw new ArgumentNullException(nameof(diagnostic));
}
_registerCodeFix(action, ImmutableArray.Create(diagnostic));
}
///
/// Add supplied to the list of fixes that will be offered to the user.
///
/// The that will be invoked to apply the fix.
/// The subset of being addressed / fixed by the .
public void RegisterCodeFix(CodeAction action, IEnumerable diagnostics)
{
if (diagnostics == null)
{
throw new ArgumentNullException(nameof(diagnostics));
}
RegisterCodeFix(action, diagnostics.ToImmutableArray());
}
///
/// Add supplied to the list of fixes that will be offered to the user.
///
/// The that will be invoked to apply the fix.
/// The subset of being addressed / fixed by the .
public void RegisterCodeFix(CodeAction action, ImmutableArray diagnostics)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
VerifyDiagnosticsArgument(diagnostics, _span);
// TODO:
// - Check that all diagnostics are unique (no duplicates).
// - Check that supplied diagnostics form subset of diagnostics originally
// passed to the provider via CodeFixContext.Diagnostics.
_registerCodeFix(action, diagnostics);
}
private static void VerifyDiagnosticsArgument(ImmutableArray diagnostics, TextSpan span)
{
if (diagnostics.IsDefault)
{
throw new ArgumentException(nameof(diagnostics));
}
if (diagnostics.Length == 0)
{
throw new ArgumentException(WorkspacesResources.At_least_one_diagnostic_must_be_supplied, nameof(diagnostics));
}
if (diagnostics.Any(d => d == null))
{
throw new ArgumentException(WorkspacesResources.Supplied_diagnostic_cannot_be_null, nameof(diagnostics));
}
if (diagnostics.Any(d => d.Location.SourceSpan != span))
{
throw new ArgumentException(string.Format(WorkspacesResources.Diagnostic_must_have_span_0, span.ToString()), nameof(diagnostics));
}
}
}
}