/src/Workspaces/Core/Portable/CodeFixes/CodeFixContext.cs

https://github.com/dotnet/roslyn · C# · 258 lines · 156 code · 33 blank · 69 comment · 20 complexity · f388aacdde855646067fda3e15a70f0b MD5 · raw file

  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. #nullable enable
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Collections.Immutable;
  8. using System.Linq;
  9. using System.Threading;
  10. using Microsoft.CodeAnalysis.CodeActions;
  11. using Microsoft.CodeAnalysis.Text;
  12. namespace Microsoft.CodeAnalysis.CodeFixes
  13. {
  14. /// <summary>
  15. /// Context for code fixes provided by a <see cref="CodeFixProvider"/>.
  16. /// </summary>
  17. public struct CodeFixContext : ITypeScriptCodeFixContext
  18. {
  19. private readonly Document _document;
  20. private readonly Project _project;
  21. private readonly TextSpan _span;
  22. private readonly ImmutableArray<Diagnostic> _diagnostics;
  23. private readonly CancellationToken _cancellationToken;
  24. private readonly Action<CodeAction, ImmutableArray<Diagnostic>> _registerCodeFix;
  25. /// <summary>
  26. /// Document corresponding to the <see cref="CodeFixContext.Span"/> to fix.
  27. /// </summary>
  28. public Document Document => _document;
  29. /// <summary>
  30. /// Project corresponding to the diagnostics to fix.
  31. /// </summary>
  32. internal Project Project => _project;
  33. /// <summary>
  34. /// Text span within the <see cref="CodeFixContext.Document"/> to fix.
  35. /// </summary>
  36. public TextSpan Span => _span;
  37. /// <summary>
  38. /// Diagnostics to fix.
  39. /// NOTE: All the diagnostics in this collection have the same <see cref="CodeFixContext.Span"/>.
  40. /// </summary>
  41. public ImmutableArray<Diagnostic> Diagnostics => _diagnostics;
  42. /// <summary>
  43. /// CancellationToken.
  44. /// </summary>
  45. public CancellationToken CancellationToken => _cancellationToken;
  46. private readonly bool _isBlocking;
  47. bool ITypeScriptCodeFixContext.IsBlocking => _isBlocking;
  48. /// <summary>
  49. /// Creates a code fix context to be passed into <see cref="CodeFixProvider.RegisterCodeFixesAsync(CodeFixContext)"/> method.
  50. /// </summary>
  51. /// <param name="document">Document to fix.</param>
  52. /// <param name="span">Text span within the <paramref name="document"/> to fix.</param>
  53. /// <param name="diagnostics">
  54. /// Diagnostics to fix.
  55. /// All the diagnostics must have the same <paramref name="span"/>.
  56. /// Additionally, the <see cref="Diagnostic.Id"/> of each diagnostic must be in the set of the <see cref="CodeFixProvider.FixableDiagnosticIds"/> of the associated <see cref="CodeFixProvider"/>.
  57. /// </param>
  58. /// <param name="registerCodeFix">Delegate to register a <see cref="CodeAction"/> fixing a subset of diagnostics.</param>
  59. /// <param name="cancellationToken">Cancellation token.</param>
  60. /// <exception cref="ArgumentNullException">Throws this exception if any of the arguments is null.</exception>
  61. /// <exception cref="ArgumentException">
  62. /// Throws this exception if the given <paramref name="diagnostics"/> is empty,
  63. /// has a null element or has an element whose span is not equal to <paramref name="span"/>.
  64. /// </exception>
  65. public CodeFixContext(
  66. Document document,
  67. TextSpan span,
  68. ImmutableArray<Diagnostic> diagnostics,
  69. Action<CodeAction, ImmutableArray<Diagnostic>> registerCodeFix,
  70. CancellationToken cancellationToken)
  71. : this(document, span, diagnostics, registerCodeFix, verifyArguments: true, cancellationToken: cancellationToken)
  72. {
  73. }
  74. /// <summary>
  75. /// Creates a code fix context to be passed into <see cref="CodeFixProvider.RegisterCodeFixesAsync(CodeFixContext)"/> method.
  76. /// </summary>
  77. /// <param name="document">Document to fix.</param>
  78. /// <param name="diagnostic">
  79. /// Diagnostic to fix.
  80. /// The <see cref="Diagnostic.Id"/> of this diagnostic must be in the set of the <see cref="CodeFixProvider.FixableDiagnosticIds"/> of the associated <see cref="CodeFixProvider"/>.
  81. /// </param>
  82. /// <param name="registerCodeFix">Delegate to register a <see cref="CodeAction"/> fixing a subset of diagnostics.</param>
  83. /// <param name="cancellationToken">Cancellation token.</param>
  84. /// <exception cref="ArgumentNullException">Throws this exception if any of the arguments is null.</exception>
  85. public CodeFixContext(
  86. Document document,
  87. Diagnostic diagnostic,
  88. Action<CodeAction, ImmutableArray<Diagnostic>> registerCodeFix,
  89. CancellationToken cancellationToken)
  90. : this(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), registerCodeFix, verifyArguments: true, cancellationToken: cancellationToken)
  91. {
  92. }
  93. internal CodeFixContext(
  94. Document document,
  95. TextSpan span,
  96. ImmutableArray<Diagnostic> diagnostics,
  97. Action<CodeAction, ImmutableArray<Diagnostic>> registerCodeFix,
  98. bool verifyArguments,
  99. CancellationToken cancellationToken)
  100. : this(document, document.Project, span, diagnostics, registerCodeFix, verifyArguments, isBlocking: false, cancellationToken)
  101. {
  102. }
  103. internal CodeFixContext(
  104. Document document,
  105. TextSpan span,
  106. ImmutableArray<Diagnostic> diagnostics,
  107. Action<CodeAction, ImmutableArray<Diagnostic>> registerCodeFix,
  108. bool verifyArguments,
  109. bool isBlocking,
  110. CancellationToken cancellationToken)
  111. : this(document, document.Project, span, diagnostics, registerCodeFix, verifyArguments, isBlocking, cancellationToken)
  112. {
  113. }
  114. private CodeFixContext(
  115. Document document,
  116. Project project,
  117. TextSpan span,
  118. ImmutableArray<Diagnostic> diagnostics,
  119. Action<CodeAction, ImmutableArray<Diagnostic>> registerCodeFix,
  120. bool verifyArguments,
  121. bool isBlocking,
  122. CancellationToken cancellationToken)
  123. {
  124. if (verifyArguments)
  125. {
  126. if (document == null)
  127. {
  128. throw new ArgumentNullException(nameof(document));
  129. }
  130. if (registerCodeFix == null)
  131. {
  132. throw new ArgumentNullException(nameof(registerCodeFix));
  133. }
  134. VerifyDiagnosticsArgument(diagnostics, span);
  135. }
  136. _document = document;
  137. _project = project;
  138. _span = span;
  139. _diagnostics = diagnostics;
  140. _registerCodeFix = registerCodeFix;
  141. _cancellationToken = cancellationToken;
  142. _isBlocking = isBlocking;
  143. }
  144. internal CodeFixContext(
  145. Document document,
  146. Diagnostic diagnostic,
  147. Action<CodeAction, ImmutableArray<Diagnostic>> registerCodeFix,
  148. bool verifyArguments,
  149. CancellationToken cancellationToken)
  150. : this(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), registerCodeFix, verifyArguments, cancellationToken)
  151. {
  152. }
  153. /// <summary>
  154. /// Add supplied <paramref name="action"/> to the list of fixes that will be offered to the user.
  155. /// </summary>
  156. /// <param name="action">The <see cref="CodeAction"/> that will be invoked to apply the fix.</param>
  157. /// <param name="diagnostic">The subset of <see cref="Diagnostics"/> being addressed / fixed by the <paramref name="action"/>.</param>
  158. public void RegisterCodeFix(CodeAction action, Diagnostic diagnostic)
  159. {
  160. if (action == null)
  161. {
  162. throw new ArgumentNullException(nameof(action));
  163. }
  164. if (diagnostic == null)
  165. {
  166. throw new ArgumentNullException(nameof(diagnostic));
  167. }
  168. _registerCodeFix(action, ImmutableArray.Create(diagnostic));
  169. }
  170. /// <summary>
  171. /// Add supplied <paramref name="action"/> to the list of fixes that will be offered to the user.
  172. /// </summary>
  173. /// <param name="action">The <see cref="CodeAction"/> that will be invoked to apply the fix.</param>
  174. /// <param name="diagnostics">The subset of <see cref="Diagnostics"/> being addressed / fixed by the <paramref name="action"/>.</param>
  175. public void RegisterCodeFix(CodeAction action, IEnumerable<Diagnostic> diagnostics)
  176. {
  177. if (diagnostics == null)
  178. {
  179. throw new ArgumentNullException(nameof(diagnostics));
  180. }
  181. RegisterCodeFix(action, diagnostics.ToImmutableArray());
  182. }
  183. /// <summary>
  184. /// Add supplied <paramref name="action"/> to the list of fixes that will be offered to the user.
  185. /// </summary>
  186. /// <param name="action">The <see cref="CodeAction"/> that will be invoked to apply the fix.</param>
  187. /// <param name="diagnostics">The subset of <see cref="Diagnostics"/> being addressed / fixed by the <paramref name="action"/>.</param>
  188. public void RegisterCodeFix(CodeAction action, ImmutableArray<Diagnostic> diagnostics)
  189. {
  190. if (action == null)
  191. {
  192. throw new ArgumentNullException(nameof(action));
  193. }
  194. VerifyDiagnosticsArgument(diagnostics, _span);
  195. // TODO:
  196. // - Check that all diagnostics are unique (no duplicates).
  197. // - Check that supplied diagnostics form subset of diagnostics originally
  198. // passed to the provider via CodeFixContext.Diagnostics.
  199. _registerCodeFix(action, diagnostics);
  200. }
  201. private static void VerifyDiagnosticsArgument(ImmutableArray<Diagnostic> diagnostics, TextSpan span)
  202. {
  203. if (diagnostics.IsDefault)
  204. {
  205. throw new ArgumentException(nameof(diagnostics));
  206. }
  207. if (diagnostics.Length == 0)
  208. {
  209. throw new ArgumentException(WorkspacesResources.At_least_one_diagnostic_must_be_supplied, nameof(diagnostics));
  210. }
  211. if (diagnostics.Any(d => d == null))
  212. {
  213. throw new ArgumentException(WorkspaceExtensionsResources.Supplied_diagnostic_cannot_be_null, nameof(diagnostics));
  214. }
  215. if (diagnostics.Any(d => d.Location.SourceSpan != span))
  216. {
  217. throw new ArgumentException(string.Format(WorkspacesResources.Diagnostic_must_have_span_0, span.ToString()), nameof(diagnostics));
  218. }
  219. }
  220. }
  221. internal interface ITypeScriptCodeFixContext
  222. {
  223. bool IsBlocking { get; }
  224. }
  225. }