PageRenderTime 24ms CodeModel.GetById 5ms RepoModel.GetById 0ms app.codeStats 0ms

/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.cs

https://gitlab.com/sharadag/TestProject2
C# | 191 lines | 154 code | 26 blank | 11 comment | 36 complexity | 9966991cc36d3d43d618c53a3a0c14a5 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.Composition;
  4. using System.Linq;
  5. using System.Threading;
  6. using Microsoft.CodeAnalysis;
  7. using Microsoft.CodeAnalysis.CSharp;
  8. using Microsoft.CodeAnalysis.CSharp.Extensions;
  9. using Microsoft.CodeAnalysis.CSharp.Syntax;
  10. using Microsoft.CodeAnalysis.Editor.Implementation.Formatting.Indentation;
  11. using Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent;
  12. using Microsoft.CodeAnalysis.Formatting;
  13. using Microsoft.CodeAnalysis.Formatting.Rules;
  14. using Microsoft.CodeAnalysis.Host;
  15. using Microsoft.CodeAnalysis.Host.Mef;
  16. using Microsoft.CodeAnalysis.Options;
  17. using Microsoft.CodeAnalysis.Shared.Extensions;
  18. using Microsoft.CodeAnalysis.Text.Shared.Extensions;
  19. using Microsoft.VisualStudio.Text;
  20. using Roslyn.Utilities;
  21. namespace Microsoft.CodeAnalysis.Editor.CSharp.Formatting.Indentation
  22. {
  23. [ExportLanguageService(typeof(IIndentationService), LanguageNames.CSharp), Shared]
  24. internal partial class CSharpIndentationService : AbstractIndentationService
  25. {
  26. private static readonly IFormattingRule s_instance = new FormattingRule();
  27. protected override IFormattingRule GetSpecializedIndentationFormattingRule()
  28. {
  29. return s_instance;
  30. }
  31. protected override AbstractIndenter GetIndenter(Document document, ITextSnapshotLine lineToBeIndented, IEnumerable<IFormattingRule> formattingRules, OptionSet optionSet, CancellationToken cancellationToken)
  32. {
  33. return new Indenter(document, formattingRules, optionSet, lineToBeIndented, cancellationToken);
  34. }
  35. protected override bool ShouldUseSmartTokenFormatterInsteadOfIndenter(
  36. IEnumerable<IFormattingRule> formattingRules,
  37. SyntaxNode root,
  38. ITextSnapshotLine line,
  39. OptionSet optionSet,
  40. CancellationToken cancellationToken)
  41. {
  42. return ShouldUseSmartTokenFormatterInsteadOfIndenter(formattingRules, (CompilationUnitSyntax)root, line, optionSet, cancellationToken);
  43. }
  44. public static bool ShouldUseSmartTokenFormatterInsteadOfIndenter(
  45. IEnumerable<IFormattingRule> formattingRules,
  46. CompilationUnitSyntax root,
  47. ITextSnapshotLine line,
  48. OptionSet optionSet,
  49. CancellationToken cancellationToken)
  50. {
  51. Contract.ThrowIfNull(formattingRules);
  52. Contract.ThrowIfNull(root);
  53. Contract.ThrowIfNull(line);
  54. if (optionSet.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp) != FormattingOptions.IndentStyle.Smart)
  55. {
  56. return false;
  57. }
  58. var firstNonWhitespacePosition = line.GetFirstNonWhitespacePosition();
  59. if (!firstNonWhitespacePosition.HasValue)
  60. {
  61. return false;
  62. }
  63. var token = root.FindToken(firstNonWhitespacePosition.Value);
  64. if (token.IsKind(SyntaxKind.None) ||
  65. token.SpanStart != firstNonWhitespacePosition)
  66. {
  67. return false;
  68. }
  69. // first see whether there is a line operation for current token
  70. var previousToken = token.GetPreviousToken(includeZeroWidth: true);
  71. // only use smart token formatter when we have two visible tokens.
  72. if (previousToken.Kind() == SyntaxKind.None || previousToken.IsMissing)
  73. {
  74. return false;
  75. }
  76. var lineOperation = FormattingOperations.GetAdjustNewLinesOperation(formattingRules, previousToken, token, optionSet);
  77. if (lineOperation != null && lineOperation.Option != AdjustNewLinesOption.ForceLinesIfOnSingleLine)
  78. {
  79. return true;
  80. }
  81. // no indentation operation, nothing to do for smart token formatter
  82. return false;
  83. }
  84. private class FormattingRule : AbstractFormattingRule
  85. {
  86. public override void AddIndentBlockOperations(List<IndentBlockOperation> list, SyntaxNode node, OptionSet optionSet, NextAction<IndentBlockOperation> nextOperation)
  87. {
  88. // these nodes should be from syntax tree from ITextSnapshot.
  89. Contract.Requires(node.SyntaxTree != null);
  90. Contract.Requires(node.SyntaxTree.GetText() != null);
  91. nextOperation.Invoke(list);
  92. ReplaceCaseIndentationRules(list, node);
  93. if (node is BaseParameterListSyntax ||
  94. node is TypeArgumentListSyntax ||
  95. node is TypeParameterListSyntax ||
  96. node.IsKind(SyntaxKind.Interpolation))
  97. {
  98. AddIndentBlockOperations(list, node);
  99. return;
  100. }
  101. var argument = node as BaseArgumentListSyntax;
  102. if (argument != null && argument.Parent.Kind() != SyntaxKind.ThisConstructorInitializer)
  103. {
  104. AddIndentBlockOperations(list, argument);
  105. return;
  106. }
  107. var constructorInitializer = node as ConstructorInitializerSyntax;
  108. if (constructorInitializer != null && constructorInitializer.ArgumentList.OpenParenToken.Kind() != SyntaxKind.None)
  109. {
  110. var text = node.SyntaxTree.GetText();
  111. // 3 different cases
  112. // first case : this or base is the first token on line
  113. // second case : colon is the first token on line
  114. var colonIsFirstTokenOnLine = !constructorInitializer.ColonToken.IsMissing && constructorInitializer.ColonToken.IsFirstTokenOnLine(text);
  115. var thisOrBaseIsFirstTokenOnLine = !constructorInitializer.ThisOrBaseKeyword.IsMissing && constructorInitializer.ThisOrBaseKeyword.IsFirstTokenOnLine(text);
  116. if (colonIsFirstTokenOnLine || thisOrBaseIsFirstTokenOnLine)
  117. {
  118. list.Add(FormattingOperations.CreateRelativeIndentBlockOperation(
  119. constructorInitializer.ThisOrBaseKeyword,
  120. constructorInitializer.ArgumentList.OpenParenToken.GetNextToken(includeZeroWidth: true),
  121. constructorInitializer.ArgumentList.CloseParenToken.GetPreviousToken(includeZeroWidth: true),
  122. indentationDelta: 1,
  123. option: IndentBlockOption.RelativePosition));
  124. }
  125. else
  126. {
  127. // third case : none of them are the first token on the line
  128. AddIndentBlockOperations(list, constructorInitializer.ArgumentList);
  129. }
  130. }
  131. }
  132. private void ReplaceCaseIndentationRules(List<IndentBlockOperation> list, SyntaxNode node)
  133. {
  134. var section = node as SwitchSectionSyntax;
  135. if (section == null || section.Statements.Count == 0)
  136. {
  137. return;
  138. }
  139. var startToken = section.Statements.First().GetFirstToken(includeZeroWidth: true);
  140. var endToken = section.Statements.Last().GetLastToken(includeZeroWidth: true);
  141. for (int i = 0; i < list.Count; i++)
  142. {
  143. var operation = list[i];
  144. if (operation.StartToken == startToken && operation.EndToken == endToken)
  145. {
  146. // replace operation
  147. list[i] = FormattingOperations.CreateIndentBlockOperation(startToken, endToken, indentationDelta: 1, option: IndentBlockOption.RelativePosition);
  148. }
  149. }
  150. }
  151. private static void AddIndentBlockOperations(List<IndentBlockOperation> list, SyntaxNode node)
  152. {
  153. // only add indent block operation if the base token is the first token on line
  154. var text = node.SyntaxTree.GetText();
  155. var baseToken = node.Parent.GetFirstToken(includeZeroWidth: true);
  156. list.Add(FormattingOperations.CreateRelativeIndentBlockOperation(
  157. baseToken,
  158. node.GetFirstToken(includeZeroWidth: true).GetNextToken(includeZeroWidth: true),
  159. node.GetLastToken(includeZeroWidth: true).GetPreviousToken(includeZeroWidth: true),
  160. indentationDelta: 1,
  161. option: IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine));
  162. }
  163. }
  164. }
  165. }