PageRenderTime 58ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Workspaces/Core/Portable/Formatting/Context/FormattingContext.InitialContextFinder.cs

https://gitlab.com/sharadag/Roslyn
C# | 200 lines | 155 code | 32 blank | 13 comment | 25 complexity | 1ae6e0c567b387121ec0f70d6ccf2501 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.Diagnostics;
  5. using System.Linq;
  6. using System.Threading;
  7. using Microsoft.CodeAnalysis;
  8. using Microsoft.CodeAnalysis.Formatting.Rules;
  9. using Microsoft.CodeAnalysis.Internal.Log;
  10. using Microsoft.CodeAnalysis.Shared.Extensions;
  11. using Microsoft.CodeAnalysis.Shared.Utilities;
  12. using Microsoft.CodeAnalysis.Text;
  13. using Roslyn.Utilities;
  14. namespace Microsoft.CodeAnalysis.Formatting
  15. {
  16. internal partial class FormattingContext
  17. {
  18. private class InitialContextFinder
  19. {
  20. private readonly TokenStream _tokenStream;
  21. private readonly ChainedFormattingRules _formattingRules;
  22. private readonly SyntaxNode _rootNode;
  23. private readonly SyntaxToken _lastToken;
  24. public InitialContextFinder(
  25. TokenStream tokenStream,
  26. ChainedFormattingRules formattingRules,
  27. SyntaxNode rootNode,
  28. SyntaxToken lastToken)
  29. {
  30. Contract.ThrowIfNull(tokenStream);
  31. Contract.ThrowIfNull(formattingRules);
  32. Contract.ThrowIfNull(rootNode);
  33. _tokenStream = tokenStream;
  34. _formattingRules = formattingRules;
  35. _rootNode = rootNode;
  36. _lastToken = lastToken;
  37. }
  38. public ValueTuple<List<IndentBlockOperation>, List<SuppressOperation>> Do(SyntaxToken startToken, SyntaxToken endToken)
  39. {
  40. // we are formatting part of document, try to find initial context that formatting will be based on such as
  41. // initial indentation and etc.
  42. using (Logger.LogBlock(FunctionId.Formatting_ContextInitialization, CancellationToken.None))
  43. {
  44. // first try to set initial indentation information
  45. var initialIndentationOperations = this.GetInitialIndentBlockOperations(startToken, endToken);
  46. // second try to set suppress wrapping regions
  47. var initialSuppressOperations = GetInitialSuppressOperations(startToken, endToken);
  48. if (initialSuppressOperations != null)
  49. {
  50. Debug.Assert(
  51. initialSuppressOperations.IsEmpty() ||
  52. initialSuppressOperations.All(
  53. o => o.TextSpan.Contains(startToken.SpanStart) ||
  54. o.TextSpan.Contains(endToken.SpanStart)));
  55. }
  56. return ValueTuple.Create(initialIndentationOperations, initialSuppressOperations);
  57. }
  58. }
  59. private List<IndentBlockOperation> GetInitialIndentBlockOperations(SyntaxToken startToken, SyntaxToken endToken)
  60. {
  61. var span = TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End);
  62. var node = startToken.GetCommonRoot(endToken).GetParentWithBiggerSpan();
  63. var previous = default(SyntaxNode);
  64. // starting from the common node, move up to the parent
  65. var operations = new List<IndentBlockOperation>();
  66. var list = new List<IndentBlockOperation>();
  67. while (node != null)
  68. {
  69. // get all operations for the nodes that contains the formatting span, but not ones contained by the span
  70. node.DescendantNodesAndSelf(n => n != previous && n.Span.IntersectsWith(span) && !span.Contains(n.Span))
  71. .Do(n =>
  72. {
  73. _formattingRules.AddIndentBlockOperations(list, n, _lastToken);
  74. foreach (var element in list)
  75. {
  76. if (element != null)
  77. {
  78. operations.Add(element);
  79. }
  80. }
  81. list.Clear();
  82. });
  83. // found some. use these as initial indentation
  84. if (operations.Any(o => o.TextSpan.Contains(span)))
  85. {
  86. break;
  87. }
  88. previous = node;
  89. node = node.Parent;
  90. }
  91. // make sure operations we have has effects over the formatting span
  92. operations.RemoveAll(o => o == null || !o.TextSpan.IntersectsWith(span));
  93. // we couldn't find anything
  94. // return initial location so that we can get base indentation correctly
  95. if (operations.Count == 0)
  96. {
  97. operations.Add(new IndentBlockOperation(
  98. startToken: _rootNode.GetFirstToken(includeZeroWidth: true),
  99. endToken: _rootNode.GetLastToken(includeZeroWidth: true),
  100. textSpan: _rootNode.FullSpan,
  101. indentationDelta: 0,
  102. option: IndentBlockOption.AbsolutePosition));
  103. return operations;
  104. }
  105. operations.Sort(CommonFormattingHelpers.IndentBlockOperationComparer);
  106. return operations;
  107. }
  108. private List<SuppressOperation> GetInitialSuppressOperations(SyntaxToken startToken, SyntaxToken endToken)
  109. {
  110. var noWrapList = this.GetInitialSuppressOperations(startToken, endToken, SuppressOption.NoWrapping);
  111. var noSpaceList = this.GetInitialSuppressOperations(startToken, endToken, SuppressOption.NoSpacing);
  112. var list = noWrapList.Combine(noSpaceList);
  113. if (list == null)
  114. {
  115. return null;
  116. }
  117. list.Sort(CommonFormattingHelpers.SuppressOperationComparer);
  118. return list;
  119. }
  120. private List<SuppressOperation> GetInitialSuppressOperations(SyntaxToken startToken, SyntaxToken endToken, SuppressOption mask)
  121. {
  122. var startList = this.GetInitialSuppressOperations(startToken, mask);
  123. var endList = this.GetInitialSuppressOperations(endToken, mask);
  124. return startList.Combine(endList);
  125. }
  126. private List<SuppressOperation> GetInitialSuppressOperations(SyntaxToken token, SuppressOption mask)
  127. {
  128. var startNode = token.Parent;
  129. var startPosition = token.SpanStart;
  130. // starting from given token, move up to root until the first meaningful
  131. // operation has found
  132. var list = new List<SuppressOperation>();
  133. Predicate<SuppressOperation> predicate = o =>
  134. {
  135. if (o == null)
  136. {
  137. return true;
  138. }
  139. if (o.ContainsElasticTrivia(_tokenStream) && !o.Option.IsOn(SuppressOption.IgnoreElastic))
  140. {
  141. return true;
  142. }
  143. if (!o.TextSpan.Contains(startPosition))
  144. {
  145. return true;
  146. }
  147. if (!o.Option.IsMaskOn(mask))
  148. {
  149. return true;
  150. }
  151. return false;
  152. };
  153. var currentIndentationNode = startNode;
  154. while (currentIndentationNode != null)
  155. {
  156. _formattingRules.AddSuppressOperations(list, currentIndentationNode, _lastToken);
  157. list.RemoveAll(predicate);
  158. if (list.Count > 0)
  159. {
  160. return list;
  161. }
  162. currentIndentationNode = currentIndentationNode.Parent;
  163. }
  164. return null;
  165. }
  166. }
  167. }
  168. }