PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs

https://gitlab.com/sharadag/Roslyn
C# | 266 lines | 172 code | 35 blank | 59 comment | 8 complexity | bcfe50658a9c0672aee7594abdcad2cc 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;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using Roslyn.Utilities;
  8. namespace Microsoft.CodeAnalysis.Editing
  9. {
  10. /// <summary>
  11. /// An editor for making changes to a syntax tree.
  12. /// </summary>
  13. public class SyntaxEditor
  14. {
  15. private readonly SyntaxGenerator _generator;
  16. private readonly SyntaxNode _root;
  17. private readonly List<Change> _changes;
  18. /// <summary>
  19. /// Creates a new <see cref="SyntaxEditor"/> instance.
  20. /// </summary>
  21. public SyntaxEditor(SyntaxNode root, Workspace workspace)
  22. {
  23. if (root == null)
  24. {
  25. throw new ArgumentNullException(nameof(root));
  26. }
  27. if (workspace == null)
  28. {
  29. throw new ArgumentNullException(nameof(workspace));
  30. }
  31. _root = root;
  32. _generator = SyntaxGenerator.GetGenerator(workspace, root.Language);
  33. _changes = new List<Change>();
  34. }
  35. /// <summary>
  36. /// The <see cref="SyntaxNode"/> that was specified when the <see cref="SyntaxEditor"/> was constructed.
  37. /// </summary>
  38. public SyntaxNode OriginalRoot
  39. {
  40. get { return _root; }
  41. }
  42. /// <summary>
  43. /// A <see cref="SyntaxGenerator"/> to use to create and change <see cref="SyntaxNode"/>'s.
  44. /// </summary>
  45. public SyntaxGenerator Generator
  46. {
  47. get { return _generator; }
  48. }
  49. /// <summary>
  50. /// Returns the changed root node.
  51. /// </summary>
  52. public SyntaxNode GetChangedRoot()
  53. {
  54. var nodes = Enumerable.Distinct(_changes.Select(c => c.Node));
  55. var newRoot = _root.TrackNodes(nodes);
  56. foreach (var change in _changes)
  57. {
  58. newRoot = change.Apply(newRoot, _generator);
  59. }
  60. return newRoot;
  61. }
  62. /// <summary>
  63. /// Makes sure the node is tracked, even if it is not changed.
  64. /// </summary>
  65. public void TrackNode(SyntaxNode node)
  66. {
  67. CheckNodeInTree(node);
  68. _changes.Add(new NoChange(node));
  69. }
  70. /// <summary>
  71. /// Remove the node from the tree.
  72. /// </summary>
  73. /// <param name="node">The node to remove that currently exists as part of the tree.</param>
  74. public void RemoveNode(SyntaxNode node)
  75. {
  76. RemoveNode(node, SyntaxGenerator.DefaultRemoveOptions);
  77. }
  78. /// <summary>
  79. /// Remove the node from the tree.
  80. /// </summary>
  81. /// <param name="node">The node to remove that currently exists as part of the tree.</param>
  82. /// <param name="options">Options that affect how node removal works.</param>
  83. public void RemoveNode(SyntaxNode node, SyntaxRemoveOptions options)
  84. {
  85. CheckNodeInTree(node);
  86. _changes.Add(new RemoveChange(node, options));
  87. }
  88. /// <summary>
  89. /// Replace the specified node with a node produced by the function.
  90. /// </summary>
  91. /// <param name="node">The node to replace that already exists in the tree.</param>
  92. /// <param name="computeReplacement">A function that computes a replacement node.
  93. /// The node passed into the compute function includes changes from prior edits. It will not appear as a descendant of the original root.</param>
  94. public void ReplaceNode(SyntaxNode node, Func<SyntaxNode, SyntaxGenerator, SyntaxNode> computeReplacement)
  95. {
  96. CheckNodeInTree(node);
  97. _changes.Add(new ReplaceChange(node, computeReplacement));
  98. }
  99. /// <summary>
  100. /// Replace the specified node with a different node.
  101. /// </summary>
  102. /// <param name="node">The node to replace that already exists in the tree.</param>
  103. /// <param name="newNode">The new node that will be placed into the tree in the existing node's location.</param>
  104. public void ReplaceNode(SyntaxNode node, SyntaxNode newNode)
  105. {
  106. CheckNodeInTree(node);
  107. if (node == newNode)
  108. {
  109. return;
  110. }
  111. this.ReplaceNode(node, (n, g) => newNode);
  112. }
  113. /// <summary>
  114. /// Insert the new nodes before the specified node already existing in the tree.
  115. /// </summary>
  116. /// <param name="node">The node already existing in the tree that the new nodes will be placed before. This must be a node this is contained within a syntax list.</param>
  117. /// <param name="newNodes">The nodes to place before the existing node. These nodes must be of a compatible type to be placed in the same list containing the existing node.</param>
  118. public void InsertBefore(SyntaxNode node, IEnumerable<SyntaxNode> newNodes)
  119. {
  120. CheckNodeInTree(node);
  121. _changes.Add(new InsertChange(node, newNodes, isBefore: true));
  122. }
  123. /// <summary>
  124. /// Insert the new node before the specified node already existing in the tree.
  125. /// </summary>
  126. /// <param name="node">The node already existing in the tree that the new nodes will be placed before. This must be a node this is contained within a syntax list.</param>
  127. /// <param name="newNode">The node to place before the existing node. This node must be of a compatible type to be placed in the same list containing the existing node.</param>
  128. public void InsertBefore(SyntaxNode node, SyntaxNode newNode)
  129. {
  130. CheckNodeInTree(node);
  131. this.InsertBefore(node, new[] { newNode });
  132. }
  133. /// <summary>
  134. /// Insert the new nodes after the specified node already existing in the tree.
  135. /// </summary>
  136. /// <param name="node">The node already existing in the tree that the new nodes will be placed after. This must be a node this is contained within a syntax list.</param>
  137. /// <param name="newNodes">The nodes to place after the existing node. These nodes must be of a compatible type to be placed in the same list containing the existing node.</param>
  138. public void InsertAfter(SyntaxNode node, IEnumerable<SyntaxNode> newNodes)
  139. {
  140. CheckNodeInTree(node);
  141. _changes.Add(new InsertChange(node, newNodes, isBefore: false));
  142. }
  143. /// <summary>
  144. /// Insert the new node after the specified node already existing in the tree.
  145. /// </summary>
  146. /// <param name="node">The node already existing in the tree that the new nodes will be placed after. This must be a node this is contained within a syntax list.</param>
  147. /// <param name="newNode">The node to place after the existing node. This node must be of a compatible type to be placed in the same list containing the existing node.</param>
  148. public void InsertAfter(SyntaxNode node, SyntaxNode newNode)
  149. {
  150. CheckNodeInTree(node);
  151. this.InsertAfter(node, new[] { newNode });
  152. }
  153. private void CheckNodeInTree(SyntaxNode node)
  154. {
  155. if (!_root.Contains(node))
  156. {
  157. throw new ArgumentException(Microsoft.CodeAnalysis.WorkspacesResources.TheNodeIsNotPartOfTheTree, nameof(node));
  158. }
  159. }
  160. private abstract class Change
  161. {
  162. internal readonly SyntaxNode Node;
  163. public Change(SyntaxNode node)
  164. {
  165. this.Node = node;
  166. }
  167. public abstract SyntaxNode Apply(SyntaxNode root, SyntaxGenerator generator);
  168. }
  169. private class NoChange : Change
  170. {
  171. public NoChange(SyntaxNode node)
  172. : base(node)
  173. {
  174. }
  175. public override SyntaxNode Apply(SyntaxNode root, SyntaxGenerator generator)
  176. {
  177. return root;
  178. }
  179. }
  180. private class RemoveChange : Change
  181. {
  182. private readonly SyntaxRemoveOptions _options;
  183. public RemoveChange(SyntaxNode node, SyntaxRemoveOptions options)
  184. : base(node)
  185. {
  186. _options = options;
  187. }
  188. public override SyntaxNode Apply(SyntaxNode root, SyntaxGenerator generator)
  189. {
  190. return generator.RemoveNode(root, root.GetCurrentNode(this.Node), _options);
  191. }
  192. }
  193. private class ReplaceChange : Change
  194. {
  195. private readonly Func<SyntaxNode, SyntaxGenerator, SyntaxNode> _modifier;
  196. public ReplaceChange(SyntaxNode node, Func<SyntaxNode, SyntaxGenerator, SyntaxNode> modifier)
  197. : base(node)
  198. {
  199. _modifier = modifier;
  200. }
  201. public override SyntaxNode Apply(SyntaxNode root, SyntaxGenerator generator)
  202. {
  203. var current = root.GetCurrentNode(this.Node);
  204. var newNode = _modifier(current, generator);
  205. return generator.ReplaceNode(root, current, newNode);
  206. }
  207. }
  208. private class InsertChange : Change
  209. {
  210. private readonly List<SyntaxNode> _newNodes;
  211. private readonly bool _isBefore;
  212. public InsertChange(SyntaxNode node, IEnumerable<SyntaxNode> newNodes, bool isBefore)
  213. : base(node)
  214. {
  215. _newNodes = newNodes.ToList();
  216. _isBefore = isBefore;
  217. }
  218. public override SyntaxNode Apply(SyntaxNode root, SyntaxGenerator generator)
  219. {
  220. if (_isBefore)
  221. {
  222. return generator.InsertNodesBefore(root, root.GetCurrentNode(this.Node), _newNodes);
  223. }
  224. else
  225. {
  226. return generator.InsertNodesAfter(root, root.GetCurrentNode(this.Node), _newNodes);
  227. }
  228. }
  229. }
  230. }
  231. }