PageRenderTime 52ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionEditHandlerService.cs

https://gitlab.com/sharadag/TestProject2
C# | 255 lines | 205 code | 34 blank | 16 comment | 35 complexity | 5c5c92791a98ec942ff479b9a1f662c3 MD5 | raw file
  1. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  2. using System.Collections.Generic;
  3. using System.ComponentModel.Composition;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Microsoft.CodeAnalysis.CodeActions;
  8. using Microsoft.CodeAnalysis.Editor.Undo;
  9. using Microsoft.CodeAnalysis.Navigation;
  10. using Microsoft.CodeAnalysis.Notification;
  11. using Microsoft.CodeAnalysis.Shared.Extensions;
  12. using Microsoft.CodeAnalysis.Text;
  13. using Roslyn.Utilities;
  14. namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeActions
  15. {
  16. [Export(typeof(ICodeActionEditHandlerService))]
  17. internal class CodeActionEditHandlerService : ICodeActionEditHandlerService
  18. {
  19. private readonly IPreviewFactoryService _previewService;
  20. private readonly IInlineRenameService _renameService;
  21. private readonly ITextBufferAssociatedViewService _associatedViewService;
  22. [ImportingConstructor]
  23. public CodeActionEditHandlerService(
  24. IPreviewFactoryService previewService,
  25. IInlineRenameService renameService,
  26. ITextBufferAssociatedViewService associatedViewService)
  27. {
  28. _previewService = previewService;
  29. _renameService = renameService;
  30. _associatedViewService = associatedViewService;
  31. }
  32. public ITextBufferAssociatedViewService AssociatedViewService
  33. {
  34. get { return _associatedViewService; }
  35. }
  36. public SolutionPreviewResult GetPreviews(Workspace workspace, IEnumerable<CodeActionOperation> operations, CancellationToken cancellationToken)
  37. {
  38. if (operations == null)
  39. {
  40. return null;
  41. }
  42. foreach (var op in operations)
  43. {
  44. cancellationToken.ThrowIfCancellationRequested();
  45. var applyChanges = op as ApplyChangesOperation;
  46. if (applyChanges != null)
  47. {
  48. var oldSolution = workspace.CurrentSolution;
  49. var newSolution = applyChanges.ChangedSolution.WithMergedLinkedFileChangesAsync(oldSolution, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken);
  50. var preview = _previewService.GetSolutionPreviews(
  51. oldSolution, newSolution, cancellationToken);
  52. if (preview != null && !preview.IsEmpty)
  53. {
  54. return preview;
  55. }
  56. }
  57. var previewOp = op as PreviewOperation;
  58. if (previewOp != null)
  59. {
  60. return new SolutionPreviewResult(new List<SolutionPreviewItem>() { new SolutionPreviewItem(projectId: null, documentId: null, lazyPreview: c => previewOp.GetPreviewAsync(c)) });
  61. }
  62. var title = op.Title;
  63. if (title != null)
  64. {
  65. return new SolutionPreviewResult(new List<SolutionPreviewItem>() { new SolutionPreviewItem(projectId: null, documentId: null, lazyPreview: c => Task.FromResult<object>(title)) });
  66. }
  67. }
  68. return null;
  69. }
  70. public void Apply(Workspace workspace, Document fromDocument, IEnumerable<CodeActionOperation> operations, string title, CancellationToken cancellationToken)
  71. {
  72. if (_renameService.ActiveSession != null)
  73. {
  74. workspace.Services.GetService<INotificationService>()?.SendNotification(
  75. EditorFeaturesResources.CannotApplyOperationWhileRenameSessionIsActive,
  76. severity: NotificationSeverity.Error);
  77. return;
  78. }
  79. #if DEBUG
  80. var documentErrorLookup = new HashSet<DocumentId>();
  81. foreach (var project in workspace.CurrentSolution.Projects)
  82. {
  83. foreach (var document in project.Documents)
  84. {
  85. if (!document.HasAnyErrors(cancellationToken).WaitAndGetResult(cancellationToken))
  86. {
  87. documentErrorLookup.Add(document.Id);
  88. }
  89. }
  90. }
  91. #endif
  92. var oldSolution = workspace.CurrentSolution;
  93. Solution updatedSolution = oldSolution;
  94. foreach (var operation in operations)
  95. {
  96. var applyChanges = operation as ApplyChangesOperation;
  97. if (applyChanges == null)
  98. {
  99. operation.Apply(workspace, cancellationToken);
  100. continue;
  101. }
  102. // there must be only one ApplyChangesOperation, we will ignore all other ones.
  103. if (updatedSolution == oldSolution)
  104. {
  105. updatedSolution = applyChanges.ChangedSolution;
  106. var projectChanges = updatedSolution.GetChanges(oldSolution).GetProjectChanges();
  107. var changedDocuments = projectChanges.SelectMany(pd => pd.GetChangedDocuments());
  108. var changedAdditionalDocuments = projectChanges.SelectMany(pd => pd.GetChangedAdditionalDocuments());
  109. var changedFiles = changedDocuments.Concat(changedAdditionalDocuments);
  110. // 0 file changes
  111. if (!changedFiles.Any())
  112. {
  113. operation.Apply(workspace, cancellationToken);
  114. continue;
  115. }
  116. // 1 file change
  117. SourceText text = null;
  118. if (!changedFiles.Skip(1).Any())
  119. {
  120. if (changedDocuments.Any())
  121. {
  122. text = oldSolution.GetDocument(changedDocuments.Single()).GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken);
  123. }
  124. else if (changedAdditionalDocuments.Any())
  125. {
  126. text = oldSolution.GetAdditionalDocument(changedAdditionalDocuments.Single()).GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken);
  127. }
  128. }
  129. if (text != null)
  130. {
  131. using (workspace.Services.GetService<ISourceTextUndoService>().RegisterUndoTransaction(text, title))
  132. {
  133. operation.Apply(workspace, cancellationToken);
  134. continue;
  135. }
  136. }
  137. // multiple file changes
  138. using (var undoTransaction = workspace.OpenGlobalUndoTransaction(title))
  139. {
  140. operation.Apply(workspace, cancellationToken);
  141. // link current file in the global undo transaction
  142. if (fromDocument != null)
  143. {
  144. undoTransaction.AddDocument(fromDocument.Id);
  145. }
  146. undoTransaction.Commit();
  147. }
  148. continue;
  149. }
  150. }
  151. #if DEBUG
  152. foreach (var project in workspace.CurrentSolution.Projects)
  153. {
  154. foreach (var document in project.Documents)
  155. {
  156. if (documentErrorLookup.Contains(document.Id))
  157. {
  158. document.VerifyNoErrorsAsync("CodeAction introduced error in error-free code", cancellationToken).Wait(cancellationToken);
  159. }
  160. }
  161. }
  162. #endif
  163. TryStartRenameSession(workspace, oldSolution, updatedSolution, cancellationToken);
  164. }
  165. private void TryStartRenameSession(Workspace workspace, Solution oldSolution, Solution newSolution, CancellationToken cancellationToken)
  166. {
  167. var changedDocuments = newSolution.GetChangedDocuments(oldSolution);
  168. foreach (var documentId in changedDocuments)
  169. {
  170. var document = newSolution.GetDocument(documentId);
  171. if (!document.SupportsSyntaxTree)
  172. {
  173. continue;
  174. }
  175. var root = document.GetSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken);
  176. var renameTokenOpt = root.GetAnnotatedNodesAndTokens(RenameAnnotation.Kind)
  177. .Where(s => s.IsToken)
  178. .Select(s => s.AsToken())
  179. .FirstOrNullable();
  180. if (renameTokenOpt.HasValue)
  181. {
  182. // It's possible that the workspace's current solution is not the same as
  183. // newSolution. This can happen if the workspace host performs other edits
  184. // during ApplyChanges, such as in the Venus scenario where indentation and
  185. // formatting can happen. To work around this, we create a SyntaxPath to the
  186. // rename token in the newSolution and resolve it to the current solution.
  187. var pathToRenameToken = new SyntaxPath(renameTokenOpt.Value);
  188. var latestDocument = workspace.CurrentSolution.GetDocument(documentId);
  189. var latestRoot = latestDocument.GetSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken);
  190. SyntaxNodeOrToken resolvedRenameToken;
  191. if (pathToRenameToken.TryResolve(latestRoot, out resolvedRenameToken) &&
  192. resolvedRenameToken.IsToken)
  193. {
  194. var editorWorkspace = workspace;
  195. var navigationService = editorWorkspace.Services.GetService<IDocumentNavigationService>();
  196. if (navigationService.TryNavigateToSpan(editorWorkspace, documentId, resolvedRenameToken.Span))
  197. {
  198. var openDocument = workspace.CurrentSolution.GetDocument(documentId);
  199. var openRoot = openDocument.GetSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken);
  200. // NOTE: We need to resolve the syntax path again in case VB line commit kicked in
  201. // due to the navigation.
  202. // TODO(DustinCa): We still have a potential problem here with VB line commit,
  203. // because it can insert tokens and all sorts of other business, which could
  204. // wind up with us not being able to resolve the token.
  205. if (pathToRenameToken.TryResolve(openRoot, out resolvedRenameToken) &&
  206. resolvedRenameToken.IsToken)
  207. {
  208. var snapshot = openDocument.GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken).FindCorrespondingEditorTextSnapshot();
  209. if (snapshot != null)
  210. {
  211. _renameService.StartInlineSession(openDocument, resolvedRenameToken.AsToken().Span, cancellationToken);
  212. }
  213. }
  214. }
  215. }
  216. return;
  217. }
  218. }
  219. }
  220. }
  221. }