From 24182e8aa7a78da9dff9c68a49350f0cf4325287 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 01:58:23 -0800 Subject: [PATCH 01/26] Pull check up, and remove the need for nullable. --- .../CSharpIndentationService.Indenter.cs | 14 +++++++------- .../AbstractIndentationService.AbstractIndenter.cs | 12 ++++-------- .../SmartIndent/AbstractIndentationService.cs | 9 ++++++++- .../VisualBasicIndentationService.Indenter.vb | 4 ++-- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs index 477bf3db20823..0a7e2450ecd2e 100644 --- a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs +++ b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs @@ -35,7 +35,7 @@ internal class Indenter : AbstractIndenter { } - protected override IndentationResult? GetDesiredIndentationWorker( + protected override IndentationResult GetDesiredIndentationWorker( SyntaxToken token, TextLine previousLine, int lastNonWhitespacePosition) { // okay, now check whether the text we found is trivia or actual token. @@ -106,7 +106,7 @@ internal class Indenter : AbstractIndenter } } - private IndentationResult? GetIndentationBasedOnToken(SyntaxToken token) + private IndentationResult GetIndentationBasedOnToken(SyntaxToken token) { Contract.ThrowIfNull(Tree); Contract.ThrowIfTrue(token.Kind() == SyntaxKind.None); @@ -254,7 +254,7 @@ internal class Indenter : AbstractIndenter } } - private IndentationResult? GetIndentationFromCommaSeparatedList(SyntaxToken token) + private IndentationResult GetIndentationFromCommaSeparatedList(SyntaxToken token) { var node = token.Parent; switch (node) @@ -276,7 +276,7 @@ internal class Indenter : AbstractIndenter return GetDefaultIndentationFromToken(token); } - private IndentationResult? GetIndentationFromCommaSeparatedList(SeparatedSyntaxList list, SyntaxToken token) where T : SyntaxNode + private IndentationResult GetIndentationFromCommaSeparatedList(SeparatedSyntaxList list, SyntaxToken token) where T : SyntaxNode { var index = list.GetWithSeparators().IndexOf(token); if (index < 0) @@ -302,7 +302,7 @@ internal class Indenter : AbstractIndenter return GetDefaultIndentationFromTokenLine(token, additionalSpace: 0); } - private IndentationResult? GetDefaultIndentationFromToken(SyntaxToken token) + private IndentationResult GetDefaultIndentationFromToken(SyntaxToken token) { if (IsPartOfQueryExpression(token)) { @@ -312,7 +312,7 @@ internal class Indenter : AbstractIndenter return GetDefaultIndentationFromTokenLine(token); } - private IndentationResult? GetIndentationForQueryExpression(SyntaxToken token) + private IndentationResult GetIndentationForQueryExpression(SyntaxToken token) { // find containing non terminal node var queryExpressionClause = GetQueryExpressionClause(token); @@ -402,7 +402,7 @@ private bool IsPartOfQueryExpression(SyntaxToken token) return queryExpression != null; } - private IndentationResult? GetDefaultIndentationFromTokenLine(SyntaxToken token, int? additionalSpace = null) + private IndentationResult GetDefaultIndentationFromTokenLine(SyntaxToken token, int? additionalSpace = null) { var spaceToAdd = additionalSpace ?? this.OptionSet.GetOption(FormattingOptions.IndentationSize, token.Language); diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs index 9a393073c46dc..f8c4a0cc1231c 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs @@ -58,14 +58,10 @@ internal abstract class AbstractIndenter tokenStream: null); } - public IndentationResult? GetDesiredIndentation(Document document) + public IndentationResult GetDesiredIndentation( + Document document, FormattingOptions.IndentStyle indentStyle) { - var indentStyle = OptionSet.GetOption(FormattingOptions.SmartIndent, document.Project.Language); - if (indentStyle == FormattingOptions.IndentStyle.None) - { - // If there is no indent style, then do nothing. - return null; - } + Debug.Assert(indentStyle != FormattingOptions.IndentStyle.None); // find previous line that is not blank. this will skip over things like preprocessor // regions and inactive code. @@ -107,7 +103,7 @@ internal abstract class AbstractIndenter token, previousNonWhitespaceOrPreprocessorLine, lastNonWhitespacePosition); } - protected abstract IndentationResult? GetDesiredIndentationWorker( + protected abstract IndentationResult GetDesiredIndentationWorker( SyntaxToken token, TextLine previousLine, int lastNonWhitespacePosition); protected IndentationResult IndentFromStartOfLine(int addedSpaces) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index 4de03c95d9252..4759872b33baa 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -51,12 +51,19 @@ private IEnumerable GetFormattingRules(Document document, int p return null; } + var indentStyle = documentOptions.GetOption(FormattingOptions.SmartIndent, document.Project.Language); + if (indentStyle == FormattingOptions.IndentStyle.None) + { + // If there is no indent style, then do nothing. + return null; + } + var indenter = GetIndenter( document.GetLanguageService(), root.SyntaxTree, lineToBeIndented, formattingRules, documentOptions, cancellationToken); - return indenter.GetDesiredIndentation(document); + return indenter.GetDesiredIndentation(document, indentStyle); } protected abstract AbstractIndenter GetIndenter( diff --git a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb index da77449d32a08..578502f91ee76 100644 --- a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb +++ b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb @@ -27,7 +27,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Formatting.Indentation Protected Overrides Function GetDesiredIndentationWorker( token As SyntaxToken, previousLine As TextLine, - lastNonWhitespacePosition As Integer) As IndentationResult? + lastNonWhitespacePosition As Integer) As IndentationResult If token.Span.End = lastNonWhitespacePosition + 1 Then Return GetIndentationBasedOnToken(token) @@ -75,7 +75,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Formatting.Indentation Return token.GetPreviousToken() End Function - Private Function GetIndentationBasedOnToken(token As SyntaxToken, Optional trivia As SyntaxTrivia = Nothing) As IndentationResult? + Private Function GetIndentationBasedOnToken(token As SyntaxToken, Optional trivia As SyntaxTrivia = Nothing) As IndentationResult Dim sourceText = LineToBeIndented.Text Dim position = GetCurrentPositionNotBelongToEndOfFileToken(LineToBeIndented.Start) From 85ecd47b190f2b0f2a748016c57c1001740bfb8e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 01:59:53 -0800 Subject: [PATCH 02/26] Move check up. --- .../SmartIndent/AbstractIndentationService.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index 4759872b33baa..6ddf41ef2c443 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -30,9 +30,16 @@ private IEnumerable GetFormattingRules(Document document, int p public IndentationResult? GetDesiredIndentation( Document document, int lineNumber, CancellationToken cancellationToken) { + var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); + var indentStyle = documentOptions.GetOption(FormattingOptions.SmartIndent, document.Project.Language); + if (indentStyle == FormattingOptions.IndentStyle.None) + { + // If there is no indent style, then do nothing. + return null; + } + var root = document.GetSyntaxRootSynchronously(cancellationToken); var sourceText = root.SyntaxTree.GetText(cancellationToken); - var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); var lineToBeIndented = sourceText.Lines[lineNumber]; @@ -51,13 +58,6 @@ private IEnumerable GetFormattingRules(Document document, int p return null; } - var indentStyle = documentOptions.GetOption(FormattingOptions.SmartIndent, document.Project.Language); - if (indentStyle == FormattingOptions.IndentStyle.None) - { - // If there is no indent style, then do nothing. - return null; - } - var indenter = GetIndenter( document.GetLanguageService(), root.SyntaxTree, lineToBeIndented, formattingRules, From 6054f7ed36bce49271fe1b0473ed9b44af90cabf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 02:02:03 -0800 Subject: [PATCH 03/26] Remove unused parameter --- .../SmartIndent/AbstractIndentationService.AbstractIndenter.cs | 3 +-- .../Implementation/SmartIndent/AbstractIndentationService.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs index f8c4a0cc1231c..0364ab27d9f31 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs @@ -58,8 +58,7 @@ internal abstract class AbstractIndenter tokenStream: null); } - public IndentationResult GetDesiredIndentation( - Document document, FormattingOptions.IndentStyle indentStyle) + public IndentationResult GetDesiredIndentation(FormattingOptions.IndentStyle indentStyle) { Debug.Assert(indentStyle != FormattingOptions.IndentStyle.None); diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index 6ddf41ef2c443..7b9d40f280084 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -63,7 +63,7 @@ private IEnumerable GetFormattingRules(Document document, int p root.SyntaxTree, lineToBeIndented, formattingRules, documentOptions, cancellationToken); - return indenter.GetDesiredIndentation(document, indentStyle); + return indenter.GetDesiredIndentation(indentStyle); } protected abstract AbstractIndenter GetIndenter( From b8a3ef50fa7ee8a16adb58c758eeae686797c5e9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 02:21:39 -0800 Subject: [PATCH 04/26] Break out method. --- .../SmartIndent/AbstractIndentationService.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index 7b9d40f280084..5904112136275 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -38,6 +39,24 @@ private IEnumerable GetFormattingRules(Document document, int p return null; } + return GetDesiredIndentation(document, lineNumber, indentStyle, cancellationToken); + } + + /// + /// Similar to except that + /// an explicit indent style can be provided that is independent of the style specified by the + /// . This indent style cannot be ; + /// + private IndentationResult? GetDesiredIndentation( + Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) + { + if (indentStyle == FormattingOptions.IndentStyle.None) + { + throw new ArgumentException(nameof(indentStyle)); + } + + var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); + var root = document.GetSyntaxRootSynchronously(cancellationToken); var sourceText = root.SyntaxTree.GetText(cancellationToken); From d552de7a5cbf7be059c57d9ee9014ea87dcc97a0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 02:28:22 -0800 Subject: [PATCH 05/26] Extract out helper. --- .../SmartIndent/AbstractIndentationService.cs | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index 5904112136275..dbfefbf26c80c 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -55,14 +55,9 @@ private IEnumerable GetFormattingRules(Document document, int p throw new ArgumentException(nameof(indentStyle)); } - var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); - - var root = document.GetSyntaxRootSynchronously(cancellationToken); - var sourceText = root.SyntaxTree.GetText(cancellationToken); - - var lineToBeIndented = sourceText.Lines[lineNumber]; - - var formattingRules = GetFormattingRules(document, lineToBeIndented.Start); + GetIndenterData(document, lineNumber, + out var documentOptions, out var root, out var lineToBeIndented, + out var formattingRules, cancellationToken); // There are two important cases for indentation. The first is when we're simply // trying to figure out the appropriate indentation on a blank line (i.e. after @@ -77,6 +72,20 @@ private IEnumerable GetFormattingRules(Document document, int p return null; } + return GetBlankLineIndentation(document, lineNumber, indentStyle, cancellationToken); + } + + /// + /// Determines indentation for a blank line (i.e. after hitting enter at the end of a line, + /// or after moving to a blank line). + /// + public IndentationResult GetBlankLineIndentation( + Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) + { + GetIndenterData(document, lineNumber, + out var documentOptions, out var root, out var lineToBeIndented, + out var formattingRules, cancellationToken); + var indenter = GetIndenter( document.GetLanguageService(), root.SyntaxTree, lineToBeIndented, formattingRules, @@ -85,6 +94,17 @@ private IEnumerable GetFormattingRules(Document document, int p return indenter.GetDesiredIndentation(indentStyle); } + private void GetIndenterData(Document document, int lineNumber, out DocumentOptionSet documentOptions, out SyntaxNode root, out TextLine lineToBeIndented, out IEnumerable formattingRules, CancellationToken cancellationToken) + { + documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); + root = document.GetSyntaxRootSynchronously(cancellationToken); + + var sourceText = root.SyntaxTree.GetText(cancellationToken); + lineToBeIndented = sourceText.Lines[lineNumber]; + + formattingRules = GetFormattingRules(document, lineToBeIndented.Start); + } + protected abstract AbstractIndenter GetIndenter( ISyntaxFactsService syntaxFacts, SyntaxTree syntaxTree, TextLine lineToBeIndented, IEnumerable formattingRules, OptionSet optionSet, CancellationToken cancellationToken); From 611ecc8fed1cfeb6f92d5bd0d2f28e98cf66e516 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 02:30:20 -0800 Subject: [PATCH 06/26] Inline method. --- .../SmartIndent/AbstractIndentationService.cs | 32 ++++++------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index dbfefbf26c80c..096e42dd9e8a5 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -31,7 +31,10 @@ private IEnumerable GetFormattingRules(Document document, int p public IndentationResult? GetDesiredIndentation( Document document, int lineNumber, CancellationToken cancellationToken) { - var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); + GetIndenterData(document, lineNumber, + out var documentOptions, out var root, out var lineToBeIndented, + out var formattingRules, cancellationToken); + var indentStyle = documentOptions.GetOption(FormattingOptions.SmartIndent, document.Project.Language); if (indentStyle == FormattingOptions.IndentStyle.None) { @@ -39,26 +42,6 @@ private IEnumerable GetFormattingRules(Document document, int p return null; } - return GetDesiredIndentation(document, lineNumber, indentStyle, cancellationToken); - } - - /// - /// Similar to except that - /// an explicit indent style can be provided that is independent of the style specified by the - /// . This indent style cannot be ; - /// - private IndentationResult? GetDesiredIndentation( - Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) - { - if (indentStyle == FormattingOptions.IndentStyle.None) - { - throw new ArgumentException(nameof(indentStyle)); - } - - GetIndenterData(document, lineNumber, - out var documentOptions, out var root, out var lineToBeIndented, - out var formattingRules, cancellationToken); - // There are two important cases for indentation. The first is when we're simply // trying to figure out the appropriate indentation on a blank line (i.e. after // hitting enter at the end of a line, or after moving to a blank line). The @@ -77,11 +60,16 @@ private IEnumerable GetFormattingRules(Document document, int p /// /// Determines indentation for a blank line (i.e. after hitting enter at the end of a line, - /// or after moving to a blank line). + /// or after moving to a blank line). This indent style cannot be ; /// public IndentationResult GetBlankLineIndentation( Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) { + if (indentStyle == FormattingOptions.IndentStyle.None) + { + throw new ArgumentException(nameof(indentStyle)); + } + GetIndenterData(document, lineNumber, out var documentOptions, out var root, out var lineToBeIndented, out var formattingRules, cancellationToken); From afbe52fd160370159561dd7da280531449855438 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 02:46:12 -0800 Subject: [PATCH 07/26] Extract out more focused interface. --- .../SmartIndent/AbstractIndentationService.cs | 2 +- .../SmartIndent/IBlankLineIndentationService.cs | 17 +++++++++++++++++ .../SmartIndent/IIndentationService.cs | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index 096e42dd9e8a5..cf787b560ffe1 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent { - internal abstract partial class AbstractIndentationService : ISynchronousIndentationService + internal abstract partial class AbstractIndentationService : ISynchronousIndentationService, IBlankLineIndentationService { protected abstract IFormattingRule GetSpecializedIndentationFormattingRule(); diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs new file mode 100644 index 0000000000000..35368cc6b2661 --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using Microsoft.CodeAnalysis.Formatting; + +namespace Microsoft.CodeAnalysis.Editor +{ + internal interface IBlankLineIndentationService + { + /// + /// Determines indentation for a blank line (i.e. after hitting enter at the end of a line, + /// or after moving to a blank line). This indent style cannot be ; + /// + IndentationResult GetBlankLineIndentation( + Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken); + } +} diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/IIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/IIndentationService.cs index d2db238f8bb20..da60e72bee21e 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/IIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/IIndentationService.cs @@ -45,6 +45,12 @@ internal interface IIndentationService : ILanguageService internal interface ISynchronousIndentationService : ILanguageService { + /// + /// Determines the desired indentation of a given line. May return null if the + /// does not want any sort of automatic indentation. May also return + /// null if the line in question is not blank and thus indentation should be deferred + /// to the formatting command handler to handle. + /// IndentationResult? GetDesiredIndentation(Document document, int lineNumber, CancellationToken cancellationToken); } } From 1a4a4754f6067858fc74109fc861202ddd949f24 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 12:13:33 -0800 Subject: [PATCH 08/26] Add comment --- .../SmartIndent/IBlankLineIndentationService.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs index 35368cc6b2661..071a4fbeb3c2a 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs @@ -9,7 +9,12 @@ internal interface IBlankLineIndentationService { /// /// Determines indentation for a blank line (i.e. after hitting enter at the end of a line, - /// or after moving to a blank line). This indent style cannot be ; + /// or after moving to a blank line). The provided cannot be + /// . + /// + /// Specifically, this function operates as if the line specified by + /// is blank. The actual contents of the line do not matter. All indentation information is + /// determined from the previous lines in the document. /// IndentationResult GetBlankLineIndentation( Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken); From 9db3d854b3cbdb4939b492b2a3e810c48835814c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 12:21:15 -0800 Subject: [PATCH 09/26] Return 'default-zero' indentation instead of throwing. --- .../AbstractIndentationService.AbstractIndenter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs index 0364ab27d9f31..16563634f3440 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs @@ -60,7 +60,11 @@ internal abstract class AbstractIndenter public IndentationResult GetDesiredIndentation(FormattingOptions.IndentStyle indentStyle) { - Debug.Assert(indentStyle != FormattingOptions.IndentStyle.None); + // If the caller wants no indent, then we'll return an effective '0' indent. + if (indentStyle == FormattingOptions.IndentStyle.None) + { + return new IndentationResult(basePosition: 0, offset: 0); + } // find previous line that is not blank. this will skip over things like preprocessor // regions and inactive code. From 655b06ccb9597a459546c040573e9f374f2e0f59 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 12:24:01 -0800 Subject: [PATCH 10/26] Handle IndentStyle.None in a better fashion. --- .../AbstractIndentationService.AbstractIndenter.cs | 7 ++----- .../SmartIndent/AbstractIndentationService.cs | 11 +++++++++-- .../SmartIndent/IBlankLineIndentationService.cs | 5 +++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs index 16563634f3440..c430ef913ab39 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs @@ -60,11 +60,8 @@ internal abstract class AbstractIndenter public IndentationResult GetDesiredIndentation(FormattingOptions.IndentStyle indentStyle) { - // If the caller wants no indent, then we'll return an effective '0' indent. - if (indentStyle == FormattingOptions.IndentStyle.None) - { - return new IndentationResult(basePosition: 0, offset: 0); - } + // Our sole caller already handled IndentStyle.None. We would not get it here. + Debug.Assert(indentStyle != FormattingOptions.IndentStyle.None); // find previous line that is not blank. this will skip over things like preprocessor // regions and inactive code. diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index cf787b560ffe1..03fa95c86a26d 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -60,14 +60,21 @@ private IEnumerable GetFormattingRules(Document document, int p /// /// Determines indentation for a blank line (i.e. after hitting enter at the end of a line, - /// or after moving to a blank line). This indent style cannot be ; + /// or after moving to a blank line). + /// + /// Specifically, this function operates as if the line specified by + /// is blank. The actual contents of the line do not matter. All indentation information is + /// determined from the previous lines in the document. + /// + /// This function will always succeed. /// public IndentationResult GetBlankLineIndentation( Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) { + // If the caller wants no indent, then we'll return an effective '0' indent. if (indentStyle == FormattingOptions.IndentStyle.None) { - throw new ArgumentException(nameof(indentStyle)); + return new IndentationResult(basePosition: 0, offset: 0); } GetIndenterData(document, lineNumber, diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs index 071a4fbeb3c2a..1d4c36a6f3193 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/IBlankLineIndentationService.cs @@ -9,12 +9,13 @@ internal interface IBlankLineIndentationService { /// /// Determines indentation for a blank line (i.e. after hitting enter at the end of a line, - /// or after moving to a blank line). The provided cannot be - /// . + /// or after moving to a blank line). /// /// Specifically, this function operates as if the line specified by /// is blank. The actual contents of the line do not matter. All indentation information is /// determined from the previous lines in the document. + /// + /// This function will always succeed. /// IndentationResult GetBlankLineIndentation( Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken); From 775e74e35dbd450157b875cc8054d583fa07ffee Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 12:39:48 -0800 Subject: [PATCH 11/26] Only get indenter once. --- .../CSharpIndentationService.Indenter.cs | 2 +- ...ractIndentationService.AbstractIndenter.cs | 38 +++++++++++-------- .../SmartIndent/AbstractIndentationService.cs | 23 +++++------ .../VisualBasicIndentationService.Indenter.vb | 4 +- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs index 0a7e2450ecd2e..8cfac58a09680 100644 --- a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs +++ b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs @@ -56,7 +56,7 @@ internal class Indenter : AbstractIndenter return IndentFromStartOfLine(0); } - var trivia = Tree.GetRoot(CancellationToken).FindTrivia(firstNonWhitespacePosition.Value, findInsideTrivia: true); + var trivia = Root.FindTrivia(firstNonWhitespacePosition.Value, findInsideTrivia: true); if (trivia.Kind() == SyntaxKind.None || this.LineToBeIndented.LineNumber > previousLine.LineNumber + 1) { // If the token belongs to the next statement and is also the first token of the statement, then it means the user wants diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs index c430ef913ab39..3af93a8768ea6 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs @@ -20,11 +20,12 @@ internal abstract partial class AbstractIndentationService { internal abstract class AbstractIndenter { - protected readonly OptionSet OptionSet; + public readonly OptionSet OptionSet; protected readonly TextLine LineToBeIndented; protected readonly int TabSize; protected readonly CancellationToken CancellationToken; + protected readonly SyntaxNode Root; protected readonly SyntaxTree Tree; protected readonly IEnumerable Rules; protected readonly BottomUpBaseIndentationFinder Finder; @@ -41,27 +42,34 @@ internal abstract class AbstractIndenter TextLine lineToBeIndented, CancellationToken cancellationToken) { - var syntaxRoot = syntaxTree.GetRoot(cancellationToken); + this.Root = syntaxTree.GetRoot(cancellationToken); this._syntaxFacts = syntaxFacts; this.OptionSet = optionSet; this.Tree = syntaxTree; this.LineToBeIndented = lineToBeIndented; - this.TabSize = this.OptionSet.GetOption(FormattingOptions.TabSize, syntaxRoot.Language); + this.TabSize = this.OptionSet.GetOption(FormattingOptions.TabSize, Root.Language); this.CancellationToken = cancellationToken; this.Rules = rules; this.Finder = new BottomUpBaseIndentationFinder( - new ChainedFormattingRules(this.Rules, OptionSet), - this.TabSize, - this.OptionSet.GetOption(FormattingOptions.IndentationSize, syntaxRoot.Language), - tokenStream: null); + new ChainedFormattingRules(this.Rules, OptionSet), + this.TabSize, + this.OptionSet.GetOption(FormattingOptions.IndentationSize, Root.Language), + tokenStream: null); } + public bool ShouldUseFormatterIfAvailable(AbstractIndentationService service) + => service.ShouldUseSmartTokenFormatterInsteadOfIndenter( + Rules, Root, LineToBeIndented, OptionSet, CancellationToken); + public IndentationResult GetDesiredIndentation(FormattingOptions.IndentStyle indentStyle) { - // Our sole caller already handled IndentStyle.None. We would not get it here. - Debug.Assert(indentStyle != FormattingOptions.IndentStyle.None); + // If the caller wants no indent, then we'll return an effective '0' indent. + if (indentStyle == FormattingOptions.IndentStyle.None) + { + return new IndentationResult(basePosition: 0, offset: 0); + } // find previous line that is not blank. this will skip over things like preprocessor // regions and inactive code. @@ -96,7 +104,7 @@ public IndentationResult GetDesiredIndentation(FormattingOptions.IndentStyle ind // able to get the last non-whitespace position. var lastNonWhitespacePosition = previousNonWhitespaceOrPreprocessorLine.GetLastNonWhitespacePosition().Value; - var token = Tree.GetRoot(CancellationToken).FindToken(lastNonWhitespacePosition); + var token = Root.FindToken(lastNonWhitespacePosition); Debug.Assert(token.RawKind != 0, "FindToken should always return a valid token"); return GetDesiredIndentationWorker( @@ -132,8 +140,7 @@ protected IndentationResult GetIndentationOfPosition(int position, int addedSpac { // Oops, the line we want to line up to is either hidden, or is in a different // visible region. - var root = this.Tree.GetRoot(CancellationToken.None); - var token = root.FindTokenFromEnd(LineToBeIndented.Start); + var token = Root.FindTokenFromEnd(LineToBeIndented.Start); var indentation = Finder.GetIndentationOfCurrentPosition(this.Tree, token, LineToBeIndented.Start, CancellationToken.None); return new IndentationResult(LineToBeIndented.Start, indentation); @@ -175,8 +182,7 @@ private TextSpan GetNormalizedSpan(int position) // No preprocessors in the entire tree, so this // line definitely doesn't have one - var root = Tree.GetRoot(CancellationToken); - if (!root.ContainsDirectives) + if (!Root.ContainsDirectives) { return sourceText.Lines[lineNumber]; } @@ -193,7 +199,7 @@ private TextSpan GetNormalizedSpan(int position) // A preprocessor directive starts on this line. if (HasPreprocessorCharacter(actualLine) && - root.DescendantTokens(actualLine.Span, tk => tk.FullWidth() > 0).Any(s_tokenHasDirective)) + Root.DescendantTokens(actualLine.Span, tk => tk.FullWidth() > 0).Any(s_tokenHasDirective)) { lineNumber--; continue; @@ -207,7 +213,7 @@ private TextSpan GetNormalizedSpan(int position) protected int GetCurrentPositionNotBelongToEndOfFileToken(int position) { - var compilationUnit = Tree.GetRoot(CancellationToken) as ICompilationUnitSyntax; + var compilationUnit = Root as ICompilationUnitSyntax; if (compilationUnit == null) { return position; diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index 03fa95c86a26d..a12cfae701ed8 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -31,11 +31,9 @@ private IEnumerable GetFormattingRules(Document document, int p public IndentationResult? GetDesiredIndentation( Document document, int lineNumber, CancellationToken cancellationToken) { - GetIndenterData(document, lineNumber, - out var documentOptions, out var root, out var lineToBeIndented, - out var formattingRules, cancellationToken); + var indenter = GetIndenter(document, lineNumber, cancellationToken); - var indentStyle = documentOptions.GetOption(FormattingOptions.SmartIndent, document.Project.Language); + var indentStyle = indenter.OptionSet.GetOption(FormattingOptions.SmartIndent, document.Project.Language); if (indentStyle == FormattingOptions.IndentStyle.None) { // If there is no indent style, then do nothing. @@ -50,12 +48,12 @@ private IEnumerable GetFormattingRules(Document document, int p // the next line). If we're in the latter case, we defer to the Formatting engine // as we need it to use all its rules to determine where the appropriate location is // for the following tokens to go. - if (ShouldUseSmartTokenFormatterInsteadOfIndenter(formattingRules, root, lineToBeIndented, documentOptions, cancellationToken)) + if (indenter.ShouldUseFormatterIfAvailable(this)) { return null; } - return GetBlankLineIndentation(document, lineNumber, indentStyle, cancellationToken); + return indenter.GetDesiredIndentation(indentStyle); } /// @@ -71,12 +69,12 @@ private IEnumerable GetFormattingRules(Document document, int p public IndentationResult GetBlankLineIndentation( Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) { - // If the caller wants no indent, then we'll return an effective '0' indent. - if (indentStyle == FormattingOptions.IndentStyle.None) - { - return new IndentationResult(basePosition: 0, offset: 0); - } + var indenter = GetIndenter(document, lineNumber, cancellationToken); + return indenter.GetDesiredIndentation(indentStyle); + } + private AbstractIndenter GetIndenter(Document document, int lineNumber, CancellationToken cancellationToken) + { GetIndenterData(document, lineNumber, out var documentOptions, out var root, out var lineToBeIndented, out var formattingRules, cancellationToken); @@ -85,8 +83,7 @@ private IEnumerable GetFormattingRules(Document document, int p document.GetLanguageService(), root.SyntaxTree, lineToBeIndented, formattingRules, documentOptions, cancellationToken); - - return indenter.GetDesiredIndentation(indentStyle); + return indenter; } private void GetIndenterData(Document document, int lineNumber, out DocumentOptionSet documentOptions, out SyntaxNode root, out TextLine lineToBeIndented, out IEnumerable formattingRules, CancellationToken cancellationToken) diff --git a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb index 578502f91ee76..e320307f4d77e 100644 --- a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb +++ b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb @@ -34,7 +34,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Formatting.Indentation Else Debug.Assert(token.FullSpan.Contains(lastNonWhitespacePosition)) - Dim trivia = Tree.GetRoot(CancellationToken).FindTrivia(lastNonWhitespacePosition) + Dim trivia = Root.FindTrivia(lastNonWhitespacePosition) ' preserve the indentation of the comment trivia before a case statement If trivia.Kind = SyntaxKind.CommentTrivia AndAlso trivia.Token.IsKind(SyntaxKind.CaseKeyword) AndAlso trivia.Token.Parent.IsKind(SyntaxKind.CaseStatement) Then @@ -56,7 +56,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Formatting.Indentation Return IndentFromStartOfLine(0) End If - Dim firstTokenOnLine = Tree.GetRoot(CancellationToken).FindToken(firstNonWhitespacePosition.Value, findInsideTrivia:=True) + Dim firstTokenOnLine = Root.FindToken(firstNonWhitespacePosition.Value, findInsideTrivia:=True) If firstTokenOnLine.Kind <> SyntaxKind.None AndAlso firstTokenOnLine.Span.Contains(firstNonWhitespacePosition.Value) Then 'okay, beginning of the line is not trivia, use this token as the base token Return GetIndentationBasedOnToken(firstTokenOnLine) From b2eed3f6632f3a385325ce955f3cbadf414e920a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 12:46:37 -0800 Subject: [PATCH 12/26] Cleanup inheritance between indentation service and indenter. --- .../Indentation/CSharpIndentationService.Indenter.cs | 4 ++++ .../Indentation/CSharpIndentationService.cs | 11 ----------- .../AbstractIndentationService.AbstractIndenter.cs | 4 +--- .../SmartIndent/AbstractIndentationService.cs | 5 +---- .../VisualBasicIndentationService.Indenter.vb | 5 +++++ .../Indentation/VisualBasicIndentationService.vb | 8 -------- 6 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs index 8cfac58a09680..579791b2640e0 100644 --- a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs +++ b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs @@ -35,6 +35,10 @@ internal class Indenter : AbstractIndenter { } + public override bool ShouldUseFormatterIfAvailable() + => ShouldUseSmartTokenFormatterInsteadOfIndenter( + Rules, (CompilationUnitSyntax)Root, LineToBeIndented, OptionSet, CancellationToken); + protected override IndentationResult GetDesiredIndentationWorker( SyntaxToken token, TextLine previousLine, int lastNonWhitespacePosition) { diff --git a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.cs b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.cs index e9d16d76c96ed..8386719ebbc58 100644 --- a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.cs +++ b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.cs @@ -40,17 +40,6 @@ protected override IFormattingRule GetSpecializedIndentationFormattingRule() optionSet, lineToBeIndented, cancellationToken); } - protected override bool ShouldUseSmartTokenFormatterInsteadOfIndenter( - IEnumerable formattingRules, - SyntaxNode root, - TextLine line, - OptionSet optionSet, - CancellationToken cancellationToken) - { - return ShouldUseSmartTokenFormatterInsteadOfIndenter( - formattingRules, (CompilationUnitSyntax)root, line, optionSet, cancellationToken); - } - public static bool ShouldUseSmartTokenFormatterInsteadOfIndenter( IEnumerable formattingRules, CompilationUnitSyntax root, diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs index 3af93a8768ea6..8502e76dc291e 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs @@ -59,9 +59,7 @@ internal abstract class AbstractIndenter tokenStream: null); } - public bool ShouldUseFormatterIfAvailable(AbstractIndentationService service) - => service.ShouldUseSmartTokenFormatterInsteadOfIndenter( - Rules, Root, LineToBeIndented, OptionSet, CancellationToken); + public abstract bool ShouldUseFormatterIfAvailable(); public IndentationResult GetDesiredIndentation(FormattingOptions.IndentStyle indentStyle) { diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index a12cfae701ed8..08dfd6d92220f 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -48,7 +48,7 @@ private IEnumerable GetFormattingRules(Document document, int p // the next line). If we're in the latter case, we defer to the Formatting engine // as we need it to use all its rules to determine where the appropriate location is // for the following tokens to go. - if (indenter.ShouldUseFormatterIfAvailable(this)) + if (indenter.ShouldUseFormatterIfAvailable()) { return null; } @@ -99,8 +99,5 @@ private void GetIndenterData(Document document, int lineNumber, out DocumentOpti protected abstract AbstractIndenter GetIndenter( ISyntaxFactsService syntaxFacts, SyntaxTree syntaxTree, TextLine lineToBeIndented, IEnumerable formattingRules, OptionSet optionSet, CancellationToken cancellationToken); - - protected abstract bool ShouldUseSmartTokenFormatterInsteadOfIndenter( - IEnumerable formattingRules, SyntaxNode root, TextLine line, OptionSet optionSet, CancellationToken cancellationToken); } } diff --git a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb index e320307f4d77e..efa937d546de7 100644 --- a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb +++ b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.Indenter.vb @@ -24,6 +24,11 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Formatting.Indentation MyBase.New(syntaxFacts, syntaxTree, rules, optionSet, line, cancellationToken) End Sub + Public Overrides Function ShouldUseFormatterIfAvailable() As Boolean + Return ShouldUseSmartTokenFormatterInsteadOfIndenter( + Rules, DirectCast(Root, CompilationUnitSyntax), LineToBeIndented, OptionSet, CancellationToken) + End Function + Protected Overrides Function GetDesiredIndentationWorker( token As SyntaxToken, previousLine As TextLine, diff --git a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.vb b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.vb index 57cec959294a0..d5e00af40e397 100644 --- a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.vb +++ b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.vb @@ -31,14 +31,6 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Formatting.Indentation Return New Indenter(syntaxFacts, syntaxTree, formattingRules, optionSet, lineToBeIndented, cancellationToken) End Function - Protected Overrides Function ShouldUseSmartTokenFormatterInsteadOfIndenter(formattingRules As IEnumerable(Of IFormattingRule), - root As SyntaxNode, - line As TextLine, - optionSet As OptionSet, - cancellationToken As CancellationToken) As Boolean - Return ShouldUseSmartTokenFormatterInsteadOfIndenter(formattingRules, DirectCast(root, CompilationUnitSyntax), line, optionSet, cancellationToken) - End Function - Public Overloads Shared Function ShouldUseSmartTokenFormatterInsteadOfIndenter( formattingRules As IEnumerable(Of IFormattingRule), root As CompilationUnitSyntax, From ed1e8a380f0454cb37bedc47e9c2dd9b5a25610b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 12:49:43 -0800 Subject: [PATCH 13/26] Fix comment. --- .../Core/Implementation/SmartIndent/IIndentationService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/IIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/IIndentationService.cs index da60e72bee21e..92ff2e89b03c1 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/IIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/IIndentationService.cs @@ -46,10 +46,10 @@ internal interface IIndentationService : ILanguageService internal interface ISynchronousIndentationService : ILanguageService { /// - /// Determines the desired indentation of a given line. May return null if the + /// Determines the desired indentation of a given line. May return if the /// does not want any sort of automatic indentation. May also return - /// null if the line in question is not blank and thus indentation should be deferred - /// to the formatting command handler to handle. + /// if the line in question is not blank and thus indentation should + /// be deferred to the formatting command handler to handle. /// IndentationResult? GetDesiredIndentation(Document document, int lineNumber, CancellationToken cancellationToken); } From 7d5ecdefcc05ca93843312d99c2c3198cd2b2de2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 12:57:24 -0800 Subject: [PATCH 14/26] Make generic to avoid later casts --- .../Indentation/CSharpIndentationService.Indenter.cs | 2 +- .../Formatting/Indentation/CSharpIndentationService.cs | 2 +- .../AbstractIndentationService.AbstractIndenter.cs | 6 +++--- .../SmartIndent/AbstractIndentationService.cs | 4 +++- .../Formatting/Indentation/VisualBasicIndentationService.vb | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs index 579791b2640e0..e521e0d161684 100644 --- a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs +++ b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.Indenter.cs @@ -37,7 +37,7 @@ internal class Indenter : AbstractIndenter public override bool ShouldUseFormatterIfAvailable() => ShouldUseSmartTokenFormatterInsteadOfIndenter( - Rules, (CompilationUnitSyntax)Root, LineToBeIndented, OptionSet, CancellationToken); + Rules, Root, LineToBeIndented, OptionSet, CancellationToken); protected override IndentationResult GetDesiredIndentationWorker( SyntaxToken token, TextLine previousLine, int lastNonWhitespacePosition) diff --git a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.cs b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.cs index 8386719ebbc58..0268da602bfe8 100644 --- a/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.cs +++ b/src/EditorFeatures/CSharp/Formatting/Indentation/CSharpIndentationService.cs @@ -23,7 +23,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.Formatting.Indentation { [ExportLanguageService(typeof(ISynchronousIndentationService), LanguageNames.CSharp), Shared] - internal partial class CSharpIndentationService : AbstractIndentationService + internal partial class CSharpIndentationService : AbstractIndentationService { private static readonly IFormattingRule s_instance = new FormattingRule(); diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs index 8502e76dc291e..d4d8afc1c120c 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent { - internal abstract partial class AbstractIndentationService + internal abstract partial class AbstractIndentationService { internal abstract class AbstractIndenter { @@ -25,7 +25,7 @@ internal abstract class AbstractIndenter protected readonly int TabSize; protected readonly CancellationToken CancellationToken; - protected readonly SyntaxNode Root; + protected readonly TSyntaxRoot Root; protected readonly SyntaxTree Tree; protected readonly IEnumerable Rules; protected readonly BottomUpBaseIndentationFinder Finder; @@ -42,7 +42,7 @@ internal abstract class AbstractIndenter TextLine lineToBeIndented, CancellationToken cancellationToken) { - this.Root = syntaxTree.GetRoot(cancellationToken); + this.Root = (TSyntaxRoot)syntaxTree.GetRoot(cancellationToken); this._syntaxFacts = syntaxFacts; this.OptionSet = optionSet; diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index 08dfd6d92220f..eca4bf44af643 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -14,7 +14,9 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent { - internal abstract partial class AbstractIndentationService : ISynchronousIndentationService, IBlankLineIndentationService + internal abstract partial class AbstractIndentationService + : ISynchronousIndentationService, IBlankLineIndentationService + where TSyntaxRoot : SyntaxNode { protected abstract IFormattingRule GetSpecializedIndentationFormattingRule(); diff --git a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.vb b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.vb index d5e00af40e397..b1c446abb45fa 100644 --- a/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.vb +++ b/src/EditorFeatures/VisualBasic/Formatting/Indentation/VisualBasicIndentationService.vb @@ -14,7 +14,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Formatting.Indentation Partial Friend Class VisualBasicIndentationService - Inherits AbstractIndentationService + Inherits AbstractIndentationService(Of CompilationUnitSyntax) Private Shared ReadOnly s_instance As IFormattingRule = New SpecialFormattingRule() From 1cfecc6203fc469965761006648c057ca2c07881 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 12:59:18 -0800 Subject: [PATCH 15/26] Add more safety. --- .../AbstractIndentationService.AbstractIndenter.cs | 11 +---------- .../SmartIndent/AbstractIndentationService.cs | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs index d4d8afc1c120c..c503886d70748 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.AbstractIndenter.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent { @@ -210,15 +209,7 @@ private TextSpan GetNormalizedSpan(int position) } protected int GetCurrentPositionNotBelongToEndOfFileToken(int position) - { - var compilationUnit = Root as ICompilationUnitSyntax; - if (compilationUnit == null) - { - return position; - } - - return Math.Min(compilationUnit.EndOfFileToken.FullSpan.Start, position); - } + => Math.Min(Root.EndOfFileToken.FullSpan.Start, position); protected bool HasPreprocessorCharacter(TextLine currentLine) { diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index eca4bf44af643..17fb9ba436a19 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent { internal abstract partial class AbstractIndentationService : ISynchronousIndentationService, IBlankLineIndentationService - where TSyntaxRoot : SyntaxNode + where TSyntaxRoot : SyntaxNode, ICompilationUnitSyntax { protected abstract IFormattingRule GetSpecializedIndentationFormattingRule(); From d3a5ad0cce025917aab36dfd8e00b2fb9f5453e0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 13:00:12 -0800 Subject: [PATCH 16/26] Remove comment that is on interface --- .../SmartIndent/AbstractIndentationService.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index 17fb9ba436a19..db9aea8834fa0 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -58,16 +58,6 @@ private IEnumerable GetFormattingRules(Document document, int p return indenter.GetDesiredIndentation(indentStyle); } - /// - /// Determines indentation for a blank line (i.e. after hitting enter at the end of a line, - /// or after moving to a blank line). - /// - /// Specifically, this function operates as if the line specified by - /// is blank. The actual contents of the line do not matter. All indentation information is - /// determined from the previous lines in the document. - /// - /// This function will always succeed. - /// public IndentationResult GetBlankLineIndentation( Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) { From adaef2ef7124782a7052ade0c72092ce6a949401 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 10 Nov 2018 13:01:02 -0800 Subject: [PATCH 17/26] Inline method --- .../SmartIndent/AbstractIndentationService.cs | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs index db9aea8834fa0..26063b01639eb 100644 --- a/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs +++ b/src/EditorFeatures/Core/Implementation/SmartIndent/AbstractIndentationService.cs @@ -67,9 +67,13 @@ private IEnumerable GetFormattingRules(Document document, int p private AbstractIndenter GetIndenter(Document document, int lineNumber, CancellationToken cancellationToken) { - GetIndenterData(document, lineNumber, - out var documentOptions, out var root, out var lineToBeIndented, - out var formattingRules, cancellationToken); + var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); + var root = document.GetSyntaxRootSynchronously(cancellationToken); + + var sourceText = root.SyntaxTree.GetText(cancellationToken); + var lineToBeIndented = sourceText.Lines[lineNumber]; + + var formattingRules = GetFormattingRules(document, lineToBeIndented.Start); var indenter = GetIndenter( document.GetLanguageService(), @@ -78,17 +82,6 @@ private AbstractIndenter GetIndenter(Document document, int lineNumber, Cancella return indenter; } - private void GetIndenterData(Document document, int lineNumber, out DocumentOptionSet documentOptions, out SyntaxNode root, out TextLine lineToBeIndented, out IEnumerable formattingRules, CancellationToken cancellationToken) - { - documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); - root = document.GetSyntaxRootSynchronously(cancellationToken); - - var sourceText = root.SyntaxTree.GetText(cancellationToken); - lineToBeIndented = sourceText.Lines[lineNumber]; - - formattingRules = GetFormattingRules(document, lineToBeIndented.Start); - } - protected abstract AbstractIndenter GetIndenter( ISyntaxFactsService syntaxFacts, SyntaxTree syntaxTree, TextLine lineToBeIndented, IEnumerable formattingRules, OptionSet optionSet, CancellationToken cancellationToken); } From bfffe11a4370616f227cd2715844c90290e5a232 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Nov 2018 12:16:33 -0800 Subject: [PATCH 18/26] Run smart indent tests throught the blank line indenter as well. --- .../Indentation/FormatterTestsBase.cs | 41 +++++++++++++++++- .../SmartIndenterEnterOnTokenTests.cs | 7 +++- .../Indentation/SmartIndenterTests.cs | 42 ++++++++++++++++--- 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/FormatterTestsBase.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/FormatterTestsBase.cs index 0c6a2d6a4951c..ebf8bb83ea62f 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/FormatterTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/FormatterTestsBase.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editor.CSharp.Formatting.Indentation; using Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Formatting; @@ -23,6 +24,7 @@ using Microsoft.VisualStudio.Text.Projection; using Moq; using Xunit; +using static Microsoft.CodeAnalysis.Formatting.FormattingOptions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting.Indentation { @@ -120,7 +122,9 @@ private static void ApplyChanges(ITextBuffer buffer, IList changes) } } - internal static void TestIndentation(int point, int? expectedIndentation, ITextView textView, TestHostDocument subjectDocument) + internal static void TestIndentation( + TestWorkspace workspace, int point, int? expectedIndentation, + ITextView textView, TestHostDocument subjectDocument) { var textUndoHistory = new Mock(); var editorOperationsFactory = new Mock(); @@ -146,9 +150,13 @@ internal static void TestIndentation(int point, int? expectedIndentation, ITextV } Assert.Equal(expectedIndentation, actualIndentation.Value); + + TestBlankLineIndentationService( + workspace, indentationLineFromBuffer.LineNumber, expectedIndentation, textView); } - public static void TestIndentation(int indentationLine, int? expectedIndentation, TestWorkspace workspace) + public static void TestIndentation( + TestWorkspace workspace, int indentationLine, int? expectedIndentation) { var snapshot = workspace.Documents.First().TextBuffer.CurrentSnapshot; var bufferGraph = new Mock(MockBehavior.Strict); @@ -183,6 +191,35 @@ public static void TestIndentation(int indentationLine, int? expectedIndentation var actualIndentation = provider.GetDesiredIndentation(indentationLineFromBuffer); Assert.Equal(expectedIndentation, actualIndentation); + + TestBlankLineIndentationService( + workspace, indentationLine, expectedIndentation, textView.Object); + } + + private static void TestBlankLineIndentationService( + TestWorkspace workspace, int indentationLine, int? expectedIndentation, ITextView textView) + { + var snapshot = workspace.Documents.First().TextBuffer.CurrentSnapshot; + var indentationLineFromBuffer = snapshot.GetLineFromLineNumber(indentationLine); + + var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); + var blankLineIndenter = (IBlankLineIndentationService)document.GetLanguageService(); + var indentStyle = workspace.Options.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp); + var blankLineIndentResult = blankLineIndenter.GetBlankLineIndentation( + document, indentationLine, indentStyle, CancellationToken.None); + + var blankLineIndentation = blankLineIndentResult.GetIndentation(textView, indentationLineFromBuffer); + if (expectedIndentation == null) + { + if (indentStyle == IndentStyle.None) + { + Assert.Equal(0, blankLineIndentation); + } + } + else + { + Assert.Equal(expectedIndentation, blankLineIndentation); + } } } } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs index dad13df1618ae..b46de92cf803d 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; +using static Microsoft.CodeAnalysis.Formatting.FormattingOptions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting.Indentation { @@ -1376,11 +1377,13 @@ void Main(object o) private async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndenterAsync( string code, int indentationLine, - int? expectedIndentation) + int? expectedIndentation, + IndentStyle indentStyle = IndentStyle.Smart) { // create tree service using (var workspace = TestWorkspace.CreateCSharp(code)) { + workspace.Options = workspace.Options.WithChangedOption(SmartIndent, LanguageNames.CSharp, indentStyle); var hostdoc = workspace.Documents.First(); var buffer = hostdoc.GetTextBuffer(); var snapshot = buffer.CurrentSnapshot; @@ -1395,7 +1398,7 @@ void Main(object o) Formatter.GetDefaultFormattingRules(workspace, root.Language), root, line.AsTextLine(), await document.GetOptionsAsync(), CancellationToken.None)); - TestIndentation(indentationLine, expectedIndentation, workspace); + TestIndentation(workspace, indentationLine, expectedIndentation); } } } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index 1a59efd061f3d..487a14e32813d 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -8,6 +8,7 @@ using Microsoft.VisualStudio.Text; using Roslyn.Test.Utilities; using Xunit; +using static Microsoft.CodeAnalysis.Formatting.FormattingOptions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting.Indentation { @@ -99,6 +100,25 @@ class Class expectedIndentation: 4); } + [WpfFact] + [Trait(Traits.Feature, Traits.Features.SmartIndent)] + public void TestExplicitNoneIndentStyle() + { + var code = @"using System; + +class Class +{ + // Comments + /// Xml Comments + +"; + AssertSmartIndent( + code, + indentationLine: 6, + expectedIndentation: null, + indentStyle: IndentStyle.None); + } + [WpfFact] [Trait(Traits.Feature, Traits.Features.SmartIndent)] public void UsingDirective() @@ -2684,7 +2704,10 @@ void M(object o) expectedIndentation: 16); } - private static void AssertSmartIndentInProjection(string markup, int expectedIndentation, CSharpParseOptions options = null) + private static void AssertSmartIndentInProjection( + string markup, int expectedIndentation, + CSharpParseOptions options = null, + IndentStyle indentStyle = IndentStyle.Smart) { var optionsSet = options != null ? new[] { options } @@ -2694,6 +2717,7 @@ private static void AssertSmartIndentInProjection(string markup, int expectedInd { using (var workspace = TestWorkspace.CreateCSharp(markup, parseOptions: option)) { + workspace.Options = workspace.Options.WithChangedOption(SmartIndent, LanguageNames.CSharp, indentStyle); var subjectDocument = workspace.Documents.Single(); var projectedDocument = @@ -2708,7 +2732,9 @@ private static void AssertSmartIndentInProjection(string markup, int expectedInd var indentationLine = projectedDocument.TextBuffer.CurrentSnapshot.GetLineFromPosition(projectedDocument.CursorPosition.Value); var point = projectedDocument.GetTextView().BufferGraph.MapDownToBuffer(indentationLine.Start, PointTrackingMode.Negative, subjectDocument.TextBuffer, PositionAffinity.Predecessor); - TestIndentation(point.Value, expectedIndentation, projectedDocument.GetTextView(), subjectDocument); + TestIndentation( + workspace, point.Value, expectedIndentation, + projectedDocument.GetTextView(), subjectDocument); } } } @@ -2717,7 +2743,8 @@ private static void AssertSmartIndentInProjection(string markup, int expectedInd string code, int indentationLine, int? expectedIndentation, - CSharpParseOptions options = null) + CSharpParseOptions options = null, + IndentStyle indentStyle = IndentStyle.Smart) { var optionsSet = options != null ? new[] { options } @@ -2727,7 +2754,8 @@ private static void AssertSmartIndentInProjection(string markup, int expectedInd { using (var workspace = TestWorkspace.CreateCSharp(code, parseOptions: option)) { - TestIndentation(indentationLine, expectedIndentation, workspace); + workspace.Options = workspace.Options.WithChangedOption(SmartIndent, LanguageNames.CSharp, indentStyle); + TestIndentation(workspace, indentationLine, expectedIndentation); } } } @@ -2735,7 +2763,8 @@ private static void AssertSmartIndentInProjection(string markup, int expectedInd private static void AssertSmartIndent( string code, int? expectedIndentation, - CSharpParseOptions options = null) + CSharpParseOptions options = null, + IndentStyle indentStyle = IndentStyle.Smart) { var optionsSet = options != null ? new[] { options } @@ -2745,9 +2774,10 @@ private static void AssertSmartIndentInProjection(string markup, int expectedInd { using (var workspace = TestWorkspace.CreateCSharp(code, parseOptions: option)) { + workspace.Options = workspace.Options.WithChangedOption(SmartIndent, LanguageNames.CSharp, indentStyle); var wpfTextView = workspace.Documents.First().GetTextView(); var line = wpfTextView.TextBuffer.CurrentSnapshot.GetLineFromPosition(wpfTextView.Caret.Position.BufferPosition).LineNumber; - TestIndentation(line, expectedIndentation, workspace); + TestIndentation(workspace, line, expectedIndentation); } } } From dc3c7cd2ad9a096ae3100eeecb4c14545b8dd7b0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Nov 2018 12:23:52 -0800 Subject: [PATCH 19/26] Make type names consistent. Extract common subclass for them to share functionality. --- ...terTestsBase.cs => CSharpFormatterTestsBase.cs} | 3 ++- .../Indentation/SmartIndenterEnterOnTokenTests.cs | 2 +- .../Formatting/Indentation/SmartIndenterTests.cs | 2 +- .../SmartTokenFormatterFormatTokenTests.cs | 2 +- .../Formatting/CoreFormatterTestsBase.cs | 14 ++++++++++++++ .../Formatting/FormattingEngineTests_Venus.vb | 2 +- ...TestBase.vb => VisualBasicFormatterTestBase.vb} | 5 ++++- 7 files changed, 24 insertions(+), 6 deletions(-) rename src/EditorFeatures/CSharpTest/Formatting/Indentation/{FormatterTestsBase.cs => CSharpFormatterTestsBase.cs} (98%) create mode 100644 src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs rename src/EditorFeatures/VisualBasicTest/Formatting/{FormattingTestBase.vb => VisualBasicFormatterTestBase.vb} (95%) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/FormatterTestsBase.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs similarity index 98% rename from src/EditorFeatures/CSharpTest/Formatting/Indentation/FormatterTestsBase.cs rename to src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs index ebf8bb83ea62f..37bed2203844d 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/FormatterTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Editor.CSharp.Formatting.Indentation; using Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.UnitTests.Formatting; using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Formatting; @@ -29,7 +30,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting.Indentation { [UseExportProvider] - public class FormatterTestsBase + public class CSharpFormatterTestsBase : CoreFormatterTestsBase { protected const string HtmlMarkup = @" diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs index b46de92cf803d..d4dd294af9776 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting.Indentation { - public class SmartIndenterEnterOnTokenTests : FormatterTestsBase + public class SmartIndenterEnterOnTokenTests : CSharpFormatterTestsBase { [WorkItem(537808, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537808")] [WpfFact] diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index 487a14e32813d..d4f60a756f3c2 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting.Indentation { - public partial class SmartIndenterTests : FormatterTestsBase + public partial class SmartIndenterTests : CSharpFormatterTestsBase { [WpfFact] [Trait(Traits.Feature, Traits.Features.SmartIndent)] diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatTokenTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatTokenTests.cs index 8c97286b060de..1e7dfad0fbb56 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatTokenTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatTokenTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting.Indentation { - public class SmartTokenFormatterFormatTokenTests : FormatterTestsBase + public class SmartTokenFormatterFormatTokenTests : CSharpFormatterTestsBase { [Fact] [Trait(Traits.Feature, Traits.Features.SmartTokenFormatting)] diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs new file mode 100644 index 0000000000000..332ce0364437c --- /dev/null +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.Formatting +{ + public class CoreFormatterTestsBase + { + } +} diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/FormattingEngineTests_Venus.vb b/src/EditorFeatures/VisualBasicTest/Formatting/FormattingEngineTests_Venus.vb index 4fd476ba5385e..530a1f6ba34ac 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/FormattingEngineTests_Venus.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/FormattingEngineTests_Venus.vb @@ -4,7 +4,7 @@ Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Formatting Public Class FormattingEngineTests_Venus - Inherits FormattingTestBase + Inherits VisualBasicFormatterTestBase Public Async Function SimpleOneLineNugget() As Threading.Tasks.Task diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/FormattingTestBase.vb b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb similarity index 95% rename from src/EditorFeatures/VisualBasicTest/Formatting/FormattingTestBase.vb rename to src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb index dcda9ce328e30..a8d4959b3c79f 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/FormattingTestBase.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb @@ -9,10 +9,13 @@ Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.Text.Shared.Extensions Imports Microsoft.VisualStudio.Text Imports Roslyn.Test.EditorUtilities +Imports Microsoft.CodeAnalysis.Editor.UnitTests.Formatting Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Formatting <[UseExportProvider]> - Public Class FormattingTestBase + Public Class VisualBasicFormatterTestBase + Inherits CoreFormatterTestsBase + Protected Async Function AssertFormatSpanAsync(content As String, expected As String, Optional baseIndentation As Integer? = Nothing, Optional span As TextSpan = Nothing) As Tasks.Task Using workspace = TestWorkspace.CreateVisualBasic(content) Dim hostdoc = workspace.Documents.First() From 8f832eeaeb4336a510fe718a2b12cb61a71b72df Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Nov 2018 12:38:05 -0800 Subject: [PATCH 20/26] Move test method down. --- .../Indentation/CSharpFormatterTestsBase.cs | 30 +-------------- .../Formatting/CoreFormatterTestsBase.cs | 37 +++++++++++++++++-- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs index 37bed2203844d..8e740fb773e8d 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs @@ -153,7 +153,7 @@ private static void ApplyChanges(ITextBuffer buffer, IList changes) Assert.Equal(expectedIndentation, actualIndentation.Value); TestBlankLineIndentationService( - workspace, indentationLineFromBuffer.LineNumber, expectedIndentation, textView); + workspace, textView, indentationLineFromBuffer.LineNumber, expectedIndentation); } public static void TestIndentation( @@ -194,33 +194,7 @@ private static void ApplyChanges(ITextBuffer buffer, IList changes) Assert.Equal(expectedIndentation, actualIndentation); TestBlankLineIndentationService( - workspace, indentationLine, expectedIndentation, textView.Object); - } - - private static void TestBlankLineIndentationService( - TestWorkspace workspace, int indentationLine, int? expectedIndentation, ITextView textView) - { - var snapshot = workspace.Documents.First().TextBuffer.CurrentSnapshot; - var indentationLineFromBuffer = snapshot.GetLineFromLineNumber(indentationLine); - - var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var blankLineIndenter = (IBlankLineIndentationService)document.GetLanguageService(); - var indentStyle = workspace.Options.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp); - var blankLineIndentResult = blankLineIndenter.GetBlankLineIndentation( - document, indentationLine, indentStyle, CancellationToken.None); - - var blankLineIndentation = blankLineIndentResult.GetIndentation(textView, indentationLineFromBuffer); - if (expectedIndentation == null) - { - if (indentStyle == IndentStyle.None) - { - Assert.Equal(0, blankLineIndentation); - } - } - else - { - Assert.Equal(expectedIndentation, blankLineIndentation); - } + workspace, textView.Object, indentationLine, expectedIndentation); } } } diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index 332ce0364437c..bdaaac50c1678 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -1,14 +1,43 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Threading; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.VisualStudio.Text.Editor; +using Xunit; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Formatting { public class CoreFormatterTestsBase { + protected static void TestBlankLineIndentationService( + TestWorkspace workspace, ITextView textView, + int indentationLine, int? expectedIndentation) + { + var snapshot = workspace.Documents.First().TextBuffer.CurrentSnapshot; + var indentationLineFromBuffer = snapshot.GetLineFromLineNumber(indentationLine); + + var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); + var blankLineIndenter = (IBlankLineIndentationService)document.GetLanguageService(); + var indentStyle = workspace.Options.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp); + var blankLineIndentResult = blankLineIndenter.GetBlankLineIndentation( + document, indentationLine, indentStyle, CancellationToken.None); + + var blankLineIndentation = blankLineIndentResult.GetIndentation(textView, indentationLineFromBuffer); + if (expectedIndentation == null) + { + if (indentStyle == FormattingOptions.IndentStyle.None) + { + Assert.Equal(0, blankLineIndentation); + } + } + else + { + Assert.Equal(expectedIndentation, blankLineIndentation); + } + } } } From 7be74cea48e15feac6fdad0fabbb9aec0bc45bf0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Nov 2018 13:15:45 -0800 Subject: [PATCH 21/26] Move test method down. --- .../Indentation/CSharpFormatterTestsBase.cs | 37 ++------------- .../Indentation/SmartIndenterTests.cs | 2 +- .../Formatting/CoreFormatterTestsBase.cs | 45 ++++++++++++++++++- .../Indentation/SmartIndenterTests.vb | 35 ++------------- .../VisualBasicFormatterTestBase.vb | 7 +++ 5 files changed, 60 insertions(+), 66 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs index 8e740fb773e8d..f4510f7167cf1 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editor.CSharp.Formatting.Indentation; +using Microsoft.CodeAnalysis.Editor.Implementation.Formatting.Indentation; using Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Formatting; @@ -39,6 +40,9 @@ public class CSharpFormatterTestsBase : CoreFormatterTestsBase "; protected const int BaseIndentationOfNugget = 8; + internal override AbstractSmartTokenFormatterCommandHandler CreateSmartTokenFormatterCommandHandler(ITextUndoHistoryRegistry registry, IEditorOperationsFactoryService operations) + => new SmartTokenFormatterCommandHandler(registry, operations); + protected static async Task GetSmartTokenFormatterIndentationWorkerAsync( TestWorkspace workspace, ITextBuffer buffer, @@ -123,39 +127,6 @@ private static void ApplyChanges(ITextBuffer buffer, IList changes) } } - internal static void TestIndentation( - TestWorkspace workspace, int point, int? expectedIndentation, - ITextView textView, TestHostDocument subjectDocument) - { - var textUndoHistory = new Mock(); - var editorOperationsFactory = new Mock(); - var editorOperations = new Mock(); - editorOperationsFactory.Setup(x => x.GetEditorOperations(textView)).Returns(editorOperations.Object); - - var snapshot = subjectDocument.TextBuffer.CurrentSnapshot; - var indentationLineFromBuffer = snapshot.GetLineFromPosition(point); - - var commandHandler = new SmartTokenFormatterCommandHandler(textUndoHistory.Object, editorOperationsFactory.Object); - commandHandler.ExecuteCommand(new ReturnKeyCommandArgs(textView, subjectDocument.TextBuffer), () => { }, TestCommandExecutionContext.Create()); - var newSnapshot = subjectDocument.TextBuffer.CurrentSnapshot; - - int? actualIndentation; - if (newSnapshot.Version.VersionNumber > snapshot.Version.VersionNumber) - { - actualIndentation = newSnapshot.GetLineFromLineNumber(indentationLineFromBuffer.LineNumber).GetFirstNonWhitespaceOffset(); - } - else - { - var provider = new SmartIndent(textView); - actualIndentation = provider.GetDesiredIndentation(indentationLineFromBuffer); - } - - Assert.Equal(expectedIndentation, actualIndentation.Value); - - TestBlankLineIndentationService( - workspace, textView, indentationLineFromBuffer.LineNumber, expectedIndentation); - } - public static void TestIndentation( TestWorkspace workspace, int indentationLine, int? expectedIndentation) { diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index d4f60a756f3c2..a4f771d733a37 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -2704,7 +2704,7 @@ void M(object o) expectedIndentation: 16); } - private static void AssertSmartIndentInProjection( + private void AssertSmartIndentInProjection( string markup, int expectedIndentation, CSharpParseOptions options = null, IndentStyle indentStyle = IndentStyle.Smart) diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index bdaaac50c1678..41717cfe95f6e 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -2,17 +2,60 @@ using System.Linq; using System.Threading; +using Microsoft.CodeAnalysis.Editor.Implementation.Formatting.Indentation; +using Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; +using Microsoft.VisualStudio.Text.Operations; +using Moq; using Xunit; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Formatting { - public class CoreFormatterTestsBase + public abstract class CoreFormatterTestsBase { + internal abstract AbstractSmartTokenFormatterCommandHandler CreateSmartTokenFormatterCommandHandler( + ITextUndoHistoryRegistry registry, IEditorOperationsFactoryService operations); + + protected void TestIndentation( + TestWorkspace workspace, int point, int? expectedIndentation, + ITextView textView, TestHostDocument subjectDocument) + { + var textUndoHistory = new Mock(); + var editorOperationsFactory = new Mock(); + var editorOperations = new Mock(); + editorOperationsFactory.Setup(x => x.GetEditorOperations(textView)).Returns(editorOperations.Object); + + var snapshot = subjectDocument.TextBuffer.CurrentSnapshot; + var indentationLineFromBuffer = snapshot.GetLineFromPosition(point); + + var commandHandler = CreateSmartTokenFormatterCommandHandler(textUndoHistory.Object, editorOperationsFactory.Object); + commandHandler.ExecuteCommandWorker(new ReturnKeyCommandArgs(textView, subjectDocument.TextBuffer), CancellationToken.None); + var newSnapshot = subjectDocument.TextBuffer.CurrentSnapshot; + + int? actualIndentation; + if (newSnapshot.Version.VersionNumber > snapshot.Version.VersionNumber) + { + actualIndentation = newSnapshot.GetLineFromLineNumber(indentationLineFromBuffer.LineNumber).GetFirstNonWhitespaceOffset(); + } + else + { + var provider = new SmartIndent(textView); + actualIndentation = provider.GetDesiredIndentation(indentationLineFromBuffer); + } + + Assert.Equal(expectedIndentation, actualIndentation.Value); + + TestBlankLineIndentationService( + workspace, textView, indentationLineFromBuffer.LineNumber, expectedIndentation); + } + protected static void TestBlankLineIndentationService( TestWorkspace workspace, ITextView textView, int indentationLine, int? expectedIndentation) diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb index d0ed844e47be6..169a0907a6eee 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb @@ -20,6 +20,8 @@ Imports Moq Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Formatting.Indentation <[UseExportProvider]> Public Class SmartIndenterTests + Inherits VisualBasicFormatterTestBase + Private Shared s_htmlMarkup As String = <html> <body> @@ -2878,7 +2880,7 @@ End Class expectedIndentation:=12) End Sub - Private Shared Sub AssertSmartIndentIndentationInProjection(markup As String, + Private Sub AssertSmartIndentIndentationInProjection(markup As String, expectedIndentation As Integer) Using workspace = TestWorkspace.CreateVisualBasic(markup) Dim subjectDocument = workspace.Documents.Single() @@ -2894,39 +2896,10 @@ End Class Dim indentationLine = projectedDocument.TextBuffer.CurrentSnapshot.GetLineFromPosition(projectedDocument.CursorPosition.Value) Dim point = projectedDocument.GetTextView().BufferGraph.MapDownToBuffer(indentationLine.Start, PointTrackingMode.Negative, subjectDocument.TextBuffer, PositionAffinity.Predecessor) - TestIndentation(point.Value, expectedIndentation, projectedDocument.GetTextView(), subjectDocument) + TestIndentation(workspace, point.Value, expectedIndentation, projectedDocument.GetTextView(), subjectDocument) End Using End Sub - Friend Shared Sub TestIndentation(point As Integer, expectedIndentation As Integer?, textView As ITextView, subjectDocument As TestHostDocument) - Dim snapshot = subjectDocument.TextBuffer.CurrentSnapshot - Dim indentationLineFromBuffer = snapshot.GetLineFromPosition(point) - Dim lineNumber = indentationLineFromBuffer.LineNumber - - Dim textUndoHistory = New Mock(Of ITextUndoHistoryRegistry) - Dim editorOperationsFactory = New Mock(Of IEditorOperationsFactoryService) - Dim editorOperations = New Mock(Of IEditorOperations) - editorOperationsFactory.Setup(Function(x) x.GetEditorOperations(textView)).Returns(editorOperations.Object) - - Dim commandHandler = New SmartTokenFormatterCommandHandler(textUndoHistory.Object, editorOperationsFactory.Object) - commandHandler.ExecuteCommandWorker(New ReturnKeyCommandArgs(textView, subjectDocument.TextBuffer), CancellationToken.None) - Dim newSnapshot = subjectDocument.TextBuffer.CurrentSnapshot - - Dim actualIndentation As Integer? - If newSnapshot.Version.VersionNumber > snapshot.Version.VersionNumber Then - actualIndentation = newSnapshot.GetLineFromLineNumber(lineNumber).GetFirstNonWhitespaceOffset() - Else - Dim provider = New SmartIndent(textView) - actualIndentation = provider.GetDesiredIndentation(indentationLineFromBuffer) - End If - - If actualIndentation Is Nothing Then - Dim x = 0 - End If - - Assert.Equal(Of Integer)(expectedIndentation.Value, actualIndentation.Value) - End Sub - ''' 0-based. The line number in code to get indentation for. Private Shared Sub AssertSmartIndent(code As String, indentationLine As Integer, expectedIndentation As Integer?, Optional indentStyle As FormattingOptions.IndentStyle = FormattingOptions.IndentStyle.Smart) Using workspace = TestWorkspace.CreateVisualBasic(code) diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb index a8d4959b3c79f..b45cf344ab379 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb @@ -10,12 +10,19 @@ Imports Microsoft.CodeAnalysis.Text.Shared.Extensions Imports Microsoft.VisualStudio.Text Imports Roslyn.Test.EditorUtilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Formatting +Imports Microsoft.CodeAnalysis.Editor.Implementation.Formatting.Indentation +Imports Microsoft.VisualStudio.Text.Operations +Imports Microsoft.CodeAnalysis.Editor.VisualBasic.Formatting.Indentation Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Formatting <[UseExportProvider]> Public Class VisualBasicFormatterTestBase Inherits CoreFormatterTestsBase + Friend Overrides Function CreateSmartTokenFormatterCommandHandler(registry As ITextUndoHistoryRegistry, operations As IEditorOperationsFactoryService) As AbstractSmartTokenFormatterCommandHandler + Return New SmartTokenFormatterCommandHandler(registry, operations) + End Function + Protected Async Function AssertFormatSpanAsync(content As String, expected As String, Optional baseIndentation As Integer? = Nothing, Optional span As TextSpan = Nothing) As Tasks.Task Using workspace = TestWorkspace.CreateVisualBasic(content) Dim hostdoc = workspace.Documents.First() From 0a369264faff09391318e6b00572d34d68dff88d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Nov 2018 13:19:08 -0800 Subject: [PATCH 22/26] Inline method --- .../Formatting/Indentation/SmartIndenterTests.vb | 10 ++-------- .../SmartTokenFormatter_FormatTokenTests.vb | 4 ++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb index 169a0907a6eee..87d4b70dfe74e 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb @@ -2903,10 +2903,9 @@ End Class ''' 0-based. The line number in code to get indentation for. Private Shared Sub AssertSmartIndent(code As String, indentationLine As Integer, expectedIndentation As Integer?, Optional indentStyle As FormattingOptions.IndentStyle = FormattingOptions.IndentStyle.Smart) Using workspace = TestWorkspace.CreateVisualBasic(code) - Dim buffer = workspace.Documents.First().GetTextBuffer() - - SetIndentStyle(buffer, indentStyle) + workspace.Options = workspace.Options.WithChangedOption(FormattingOptions.SmartIndent, LanguageNames.VisualBasic, indentStyle) + Dim buffer = workspace.Documents.First().GetTextBuffer() Dim bufferGraph = New Mock(Of IBufferGraph)(MockBehavior.Strict) bufferGraph.Setup(Function(x) x.MapUpToSnapshot(It.IsAny(Of SnapshotPoint)(), It.IsAny(Of PointTrackingMode)(), @@ -2945,10 +2944,5 @@ End Class End Using End Using End Sub - - Friend Shared Sub SetIndentStyle(buffer As ITextBuffer, indentStyle As FormattingOptions.IndentStyle) - Dim workspace = buffer.GetWorkspace() - workspace.Options = workspace.Options.WithChangedOption(FormattingOptions.SmartIndent, LanguageNames.VisualBasic, indentStyle) - End Sub End Class End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb index 4b61d0ae8c533..79e3fc25f4fdb 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb @@ -176,11 +176,11 @@ End Class MarkupTestFile.GetPosition(codeWithMarkup, code, position) Using workspace = TestWorkspace.CreateVisualBasic(code) + workspace.Options = workspace.Options.WithChangedOption(FormattingOptions.SmartIndent, LanguageNames.VisualBasic, indentStyle) + Dim hostdoc = workspace.Documents.First() Dim buffer = hostdoc.GetTextBuffer() - SmartIndenterTests.SetIndentStyle(buffer, indentStyle) - Dim snapshot = buffer.CurrentSnapshot Dim line = snapshot.GetLineFromPosition(position) From 62a9d0cd7b9e8492b30a31540d9b007ba1f8eb6c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Nov 2018 13:34:38 -0800 Subject: [PATCH 23/26] Share more code between C# and VB. --- .../Indentation/CSharpFormatterTestsBase.cs | 44 ++--------------- .../Indentation/SmartIndenterTests.cs | 4 +- .../Formatting/CoreFormatterTestsBase.cs | 49 ++++++++++++++++++- .../Indentation/SmartIndenterTests.vb | 40 +-------------- .../VisualBasicFormatterTestBase.vb | 4 ++ 5 files changed, 58 insertions(+), 83 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs index f4510f7167cf1..370169208ba22 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs @@ -40,6 +40,9 @@ public class CSharpFormatterTestsBase : CoreFormatterTestsBase "; protected const int BaseIndentationOfNugget = 8; + internal override string GetLanguageName() + => LanguageNames.CSharp; + internal override AbstractSmartTokenFormatterCommandHandler CreateSmartTokenFormatterCommandHandler(ITextUndoHistoryRegistry registry, IEditorOperationsFactoryService operations) => new SmartTokenFormatterCommandHandler(registry, operations); @@ -126,46 +129,5 @@ private static void ApplyChanges(ITextBuffer buffer, IList changes) return await GetSmartTokenFormatterIndentationWorkerAsync(workspace, buffer, indentationLine, ch); } } - - public static void TestIndentation( - TestWorkspace workspace, int indentationLine, int? expectedIndentation) - { - var snapshot = workspace.Documents.First().TextBuffer.CurrentSnapshot; - var bufferGraph = new Mock(MockBehavior.Strict); - bufferGraph.Setup(x => x.MapUpToSnapshot(It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((p, m, a, s) => - { - - if (workspace.Services.GetService() is TestFormattingRuleFactoryServiceFactory.Factory factory && factory.BaseIndentation != 0 && factory.TextSpan.Contains(p.Position)) - { - var line = p.GetContainingLine(); - var projectedOffset = line.GetFirstNonWhitespaceOffset().Value - factory.BaseIndentation; - return new SnapshotPoint(p.Snapshot, p.Position - projectedOffset); - } - - return p; - }); - - var projectionBuffer = new Mock(MockBehavior.Strict); - projectionBuffer.Setup(x => x.ContentType.DisplayName).Returns("None"); - - var textView = new Mock(MockBehavior.Strict); - textView.Setup(x => x.Options).Returns(TestEditorOptions.Instance); - textView.Setup(x => x.BufferGraph).Returns(bufferGraph.Object); - textView.SetupGet(x => x.TextSnapshot.TextBuffer).Returns(projectionBuffer.Object); - - var provider = new SmartIndent(textView.Object); - - var indentationLineFromBuffer = snapshot.GetLineFromLineNumber(indentationLine); - var actualIndentation = provider.GetDesiredIndentation(indentationLineFromBuffer); - - Assert.Equal(expectedIndentation, actualIndentation); - - TestBlankLineIndentationService( - workspace, textView.Object, indentationLine, expectedIndentation); - } } } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index a4f771d733a37..06d21824664a6 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -2739,7 +2739,7 @@ void M(object o) } } - private static void AssertSmartIndent( + private void AssertSmartIndent( string code, int indentationLine, int? expectedIndentation, @@ -2760,7 +2760,7 @@ void M(object o) } } - private static void AssertSmartIndent( + private void AssertSmartIndent( string code, int? expectedIndentation, CSharpParseOptions options = null, diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index 41717cfe95f6e..36782c5bc7a97 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -8,11 +8,14 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Text.Operations; +using Microsoft.VisualStudio.Text.Projection; using Moq; using Xunit; @@ -20,6 +23,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Formatting { public abstract class CoreFormatterTestsBase { + internal abstract string GetLanguageName(); internal abstract AbstractSmartTokenFormatterCommandHandler CreateSmartTokenFormatterCommandHandler( ITextUndoHistoryRegistry registry, IEditorOperationsFactoryService operations); @@ -56,7 +60,7 @@ public abstract class CoreFormatterTestsBase workspace, textView, indentationLineFromBuffer.LineNumber, expectedIndentation); } - protected static void TestBlankLineIndentationService( + protected void TestBlankLineIndentationService( TestWorkspace workspace, ITextView textView, int indentationLine, int? expectedIndentation) { @@ -65,7 +69,7 @@ public abstract class CoreFormatterTestsBase var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); var blankLineIndenter = (IBlankLineIndentationService)document.GetLanguageService(); - var indentStyle = workspace.Options.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp); + var indentStyle = workspace.Options.GetOption(FormattingOptions.SmartIndent, GetLanguageName()); var blankLineIndentResult = blankLineIndenter.GetBlankLineIndentation( document, indentationLine, indentStyle, CancellationToken.None); @@ -82,5 +86,46 @@ public abstract class CoreFormatterTestsBase Assert.Equal(expectedIndentation, blankLineIndentation); } } + + protected void TestIndentation( + TestWorkspace workspace, int indentationLine, int? expectedIndentation) + { + var snapshot = workspace.Documents.First().TextBuffer.CurrentSnapshot; + var bufferGraph = new Mock(MockBehavior.Strict); + bufferGraph.Setup(x => x.MapUpToSnapshot(It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((p, m, a, s) => + { + + if (workspace.Services.GetService() is TestFormattingRuleFactoryServiceFactory.Factory factory && factory.BaseIndentation != 0 && factory.TextSpan.Contains(p.Position)) + { + var line = p.GetContainingLine(); + var projectedOffset = line.GetFirstNonWhitespaceOffset().Value - factory.BaseIndentation; + return new SnapshotPoint(p.Snapshot, p.Position - projectedOffset); + } + + return p; + }); + + var projectionBuffer = new Mock(MockBehavior.Strict); + projectionBuffer.Setup(x => x.ContentType.DisplayName).Returns("None"); + + var textView = new Mock(MockBehavior.Strict); + textView.Setup(x => x.Options).Returns(TestEditorOptions.Instance); + textView.Setup(x => x.BufferGraph).Returns(bufferGraph.Object); + textView.SetupGet(x => x.TextSnapshot.TextBuffer).Returns(projectionBuffer.Object); + + var provider = new SmartIndent(textView.Object); + + var indentationLineFromBuffer = snapshot.GetLineFromLineNumber(indentationLine); + var actualIndentation = provider.GetDesiredIndentation(indentationLineFromBuffer); + + Assert.Equal(expectedIndentation, actualIndentation); + + TestBlankLineIndentationService( + workspace, textView.Object, indentationLine, expectedIndentation); + } } } diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb index 87d4b70dfe74e..1a147b6c8a7da 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb @@ -2901,47 +2901,11 @@ End Class End Sub ''' 0-based. The line number in code to get indentation for. - Private Shared Sub AssertSmartIndent(code As String, indentationLine As Integer, expectedIndentation As Integer?, Optional indentStyle As FormattingOptions.IndentStyle = FormattingOptions.IndentStyle.Smart) + Private Sub AssertSmartIndent(code As String, indentationLine As Integer, expectedIndentation As Integer?, Optional indentStyle As FormattingOptions.IndentStyle = FormattingOptions.IndentStyle.Smart) Using workspace = TestWorkspace.CreateVisualBasic(code) workspace.Options = workspace.Options.WithChangedOption(FormattingOptions.SmartIndent, LanguageNames.VisualBasic, indentStyle) - Dim buffer = workspace.Documents.First().GetTextBuffer() - Dim bufferGraph = New Mock(Of IBufferGraph)(MockBehavior.Strict) - bufferGraph.Setup(Function(x) x.MapUpToSnapshot(It.IsAny(Of SnapshotPoint)(), - It.IsAny(Of PointTrackingMode)(), - It.IsAny(Of PositionAffinity)(), - It.IsAny(Of ITextSnapshot))). - Returns(Of SnapshotPoint, PointTrackingMode, PositionAffinity, ITextSnapshot)( - Function(p, m, a, s) - Dim factory = TryCast(workspace.Services.GetService(Of IHostDependentFormattingRuleFactoryService)(), - TestFormattingRuleFactoryServiceFactory.Factory) - - If factory IsNot Nothing AndAlso factory.BaseIndentation <> 0 AndAlso factory.TextSpan.Contains(p.Position) Then - Dim line = p.GetContainingLine() - Dim projectedOffset = line.GetFirstNonWhitespaceOffset().Value - factory.BaseIndentation - Return New SnapshotPoint(p.Snapshot, p.Position - projectedOffset) - End If - - Return p - End Function) - - WpfTestRunner.RequireWpfFact($"Test helper creates mocks of {NameOf(ITextView)}") - - Dim textView = New Mock(Of ITextView)(MockBehavior.Strict) - textView.Setup(Function(x) x.Options).Returns(TestEditorOptions.Instance) - textView.Setup(Function(x) x.BufferGraph).Returns(bufferGraph.Object) - textView.SetupGet(Function(x) x.TextSnapshot).Returns(buffer.CurrentSnapshot) - - Using indenter = New SmartIndent(textView.Object) - Dim indentationLineFromBuffer = buffer.CurrentSnapshot.GetLineFromLineNumber(indentationLine) - Dim actualIndentation = indenter.GetDesiredIndentation(indentationLineFromBuffer) - - If expectedIndentation.HasValue Then - Assert.Equal(Of Integer)(expectedIndentation.Value, actualIndentation.Value) - Else - Assert.Null(actualIndentation) - End If - End Using + TestIndentation(workspace, indentationLine, expectedIndentation) End Using End Sub End Class diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb index b45cf344ab379..140ff97219cb8 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb @@ -19,6 +19,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Formatting Public Class VisualBasicFormatterTestBase Inherits CoreFormatterTestsBase + Friend Overrides Function GetLanguageName() As String + Return LanguageNames.VisualBasic + End Function + Friend Overrides Function CreateSmartTokenFormatterCommandHandler(registry As ITextUndoHistoryRegistry, operations As IEditorOperationsFactoryService) As AbstractSmartTokenFormatterCommandHandler Return New SmartTokenFormatterCommandHandler(registry, operations) End Function From 151775c259121c20b832745408761b8304224027 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Nov 2018 13:43:33 -0800 Subject: [PATCH 24/26] Update tests to be more declarative about their expectations. --- .../SmartIndenterEnterOnTokenTests.cs | 5 +++- .../Indentation/SmartIndenterTests.cs | 18 ++++++++++--- .../Formatting/CoreFormatterTestsBase.cs | 26 +++++++------------ .../Indentation/SmartIndenterTests.vb | 22 ++++++++++++---- 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs index d4dd294af9776..12db6f46b9738 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs @@ -1378,6 +1378,7 @@ void Main(object o) string code, int indentationLine, int? expectedIndentation, + int? expectedBlankLineIndentation = null, IndentStyle indentStyle = IndentStyle.Smart) { // create tree service @@ -1398,7 +1399,9 @@ void Main(object o) Formatter.GetDefaultFormattingRules(workspace, root.Language), root, line.AsTextLine(), await document.GetOptionsAsync(), CancellationToken.None)); - TestIndentation(workspace, indentationLine, expectedIndentation); + TestIndentation( + workspace, indentationLine, + expectedIndentation, expectedBlankLineIndentation); } } } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index 06d21824664a6..2d44323431055 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -116,6 +116,7 @@ class Class code, indentationLine: 6, expectedIndentation: null, + expectedBlankLineIndentation: 0, indentStyle: IndentStyle.None); } @@ -412,7 +413,8 @@ void Method() AssertSmartIndent( code, indentationLine: 7, - expectedIndentation: null); + expectedIndentation: null, + expectedBlankLineIndentation: 0); } [WpfFact] @@ -2706,6 +2708,7 @@ void M(object o) private void AssertSmartIndentInProjection( string markup, int expectedIndentation, + int? expectedBlankLineIndentation = null, CSharpParseOptions options = null, IndentStyle indentStyle = IndentStyle.Smart) { @@ -2733,7 +2736,8 @@ void M(object o) var point = projectedDocument.GetTextView().BufferGraph.MapDownToBuffer(indentationLine.Start, PointTrackingMode.Negative, subjectDocument.TextBuffer, PositionAffinity.Predecessor); TestIndentation( - workspace, point.Value, expectedIndentation, + workspace, point.Value, + expectedIndentation, expectedBlankLineIndentation, projectedDocument.GetTextView(), subjectDocument); } } @@ -2743,6 +2747,7 @@ void M(object o) string code, int indentationLine, int? expectedIndentation, + int? expectedBlankLineIndentation = null, CSharpParseOptions options = null, IndentStyle indentStyle = IndentStyle.Smart) { @@ -2755,7 +2760,9 @@ void M(object o) using (var workspace = TestWorkspace.CreateCSharp(code, parseOptions: option)) { workspace.Options = workspace.Options.WithChangedOption(SmartIndent, LanguageNames.CSharp, indentStyle); - TestIndentation(workspace, indentationLine, expectedIndentation); + TestIndentation( + workspace, indentationLine, + expectedIndentation, expectedBlankLineIndentation); } } } @@ -2763,6 +2770,7 @@ void M(object o) private void AssertSmartIndent( string code, int? expectedIndentation, + int? expectedBlankLineIndentation = null, CSharpParseOptions options = null, IndentStyle indentStyle = IndentStyle.Smart) { @@ -2777,7 +2785,9 @@ void M(object o) workspace.Options = workspace.Options.WithChangedOption(SmartIndent, LanguageNames.CSharp, indentStyle); var wpfTextView = workspace.Documents.First().GetTextView(); var line = wpfTextView.TextBuffer.CurrentSnapshot.GetLineFromPosition(wpfTextView.Caret.Position.BufferPosition).LineNumber; - TestIndentation(workspace, line, expectedIndentation); + TestIndentation( + workspace, line, + expectedIndentation, expectedBlankLineIndentation); } } } diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index 36782c5bc7a97..4f9eb2713e17f 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -28,7 +28,8 @@ public abstract class CoreFormatterTestsBase ITextUndoHistoryRegistry registry, IEditorOperationsFactoryService operations); protected void TestIndentation( - TestWorkspace workspace, int point, int? expectedIndentation, + TestWorkspace workspace, int point, + int? expectedIndentation, int? expectedBlankLineIndentation, ITextView textView, TestHostDocument subjectDocument) { var textUndoHistory = new Mock(); @@ -57,12 +58,13 @@ public abstract class CoreFormatterTestsBase Assert.Equal(expectedIndentation, actualIndentation.Value); TestBlankLineIndentationService( - workspace, textView, indentationLineFromBuffer.LineNumber, expectedIndentation); + workspace, textView, indentationLineFromBuffer.LineNumber, + expectedBlankLineIndentation ?? expectedIndentation.Value); } protected void TestBlankLineIndentationService( TestWorkspace workspace, ITextView textView, - int indentationLine, int? expectedIndentation) + int indentationLine, int expectedIndentation) { var snapshot = workspace.Documents.First().TextBuffer.CurrentSnapshot; var indentationLineFromBuffer = snapshot.GetLineFromLineNumber(indentationLine); @@ -74,21 +76,12 @@ public abstract class CoreFormatterTestsBase document, indentationLine, indentStyle, CancellationToken.None); var blankLineIndentation = blankLineIndentResult.GetIndentation(textView, indentationLineFromBuffer); - if (expectedIndentation == null) - { - if (indentStyle == FormattingOptions.IndentStyle.None) - { - Assert.Equal(0, blankLineIndentation); - } - } - else - { - Assert.Equal(expectedIndentation, blankLineIndentation); - } + Assert.Equal(expectedIndentation, blankLineIndentation); } protected void TestIndentation( - TestWorkspace workspace, int indentationLine, int? expectedIndentation) + TestWorkspace workspace, int indentationLine, + int? expectedIndentation, int? expectedBlankLineIndentation) { var snapshot = workspace.Documents.First().TextBuffer.CurrentSnapshot; var bufferGraph = new Mock(MockBehavior.Strict); @@ -125,7 +118,8 @@ public abstract class CoreFormatterTestsBase Assert.Equal(expectedIndentation, actualIndentation); TestBlankLineIndentationService( - workspace, textView.Object, indentationLine, expectedIndentation); + workspace, textView.Object, indentationLine, + expectedBlankLineIndentation ?? expectedIndentation.Value); } } } diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb index 1a147b6c8a7da..02792668a4e0e 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb @@ -2493,6 +2493,7 @@ End Namespace code, indentationLine:=3, expectedIndentation:=Nothing, + expectedBlankLineIndentation:=0, indentStyle:=FormattingOptions.IndentStyle.None) End Sub @@ -2880,8 +2881,10 @@ End Class expectedIndentation:=12) End Sub - Private Sub AssertSmartIndentIndentationInProjection(markup As String, - expectedIndentation As Integer) + Private Sub AssertSmartIndentIndentationInProjection( + markup As String, + expectedIndentation As Integer, + Optional expectedBlankLineIndentation As Integer? = Nothing) Using workspace = TestWorkspace.CreateVisualBasic(markup) Dim subjectDocument = workspace.Documents.Single() Dim projectedDocument = workspace.CreateProjectionBufferDocument(s_htmlMarkup, workspace.Documents, LanguageNames.CSharp) @@ -2896,16 +2899,25 @@ End Class Dim indentationLine = projectedDocument.TextBuffer.CurrentSnapshot.GetLineFromPosition(projectedDocument.CursorPosition.Value) Dim point = projectedDocument.GetTextView().BufferGraph.MapDownToBuffer(indentationLine.Start, PointTrackingMode.Negative, subjectDocument.TextBuffer, PositionAffinity.Predecessor) - TestIndentation(workspace, point.Value, expectedIndentation, projectedDocument.GetTextView(), subjectDocument) + TestIndentation( + workspace, point.Value, + expectedIndentation, expectedBlankLineIndentation, + projectedDocument.GetTextView(), subjectDocument) End Using End Sub ''' 0-based. The line number in code to get indentation for. - Private Sub AssertSmartIndent(code As String, indentationLine As Integer, expectedIndentation As Integer?, Optional indentStyle As FormattingOptions.IndentStyle = FormattingOptions.IndentStyle.Smart) + Private Sub AssertSmartIndent( + code As String, indentationLine As Integer, + expectedIndentation As Integer?, + Optional expectedBlankLineIndentation As Integer? = Nothing, + Optional indentStyle As FormattingOptions.IndentStyle = FormattingOptions.IndentStyle.Smart) Using workspace = TestWorkspace.CreateVisualBasic(code) workspace.Options = workspace.Options.WithChangedOption(FormattingOptions.SmartIndent, LanguageNames.VisualBasic, indentStyle) - TestIndentation(workspace, indentationLine, expectedIndentation) + TestIndentation( + workspace, indentationLine, + expectedIndentation, expectedBlankLineIndentation) End Using End Sub End Class From 4aef67f34d95e80f8f26fff5605ca41978bb9003 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Nov 2018 13:48:14 -0800 Subject: [PATCH 25/26] Add comments. --- .../Formatting/Indentation/SmartIndenterTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index 2d44323431055..7fbba8a1bb0af 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -410,11 +410,16 @@ void Method() code, indentationLine: 6, expectedIndentation: 12); + + // This is the line after the method call. ISynchronousIndentationService will bail in + // this case as it thinks this is a case for "smart formatting". However, + // IBlankLineIndentationService appropriately picks 8 columns as the location to indent + // to. AssertSmartIndent( code, indentationLine: 7, expectedIndentation: null, - expectedBlankLineIndentation: 0); + expectedBlankLineIndentation: 8); } [WpfFact] From 71efab2e73d02ac7837154bf3705e9de11ed7d7e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 11 Nov 2018 14:27:44 -0800 Subject: [PATCH 26/26] Fix up test code. --- .../Test/MetadataAsSource/MetadataAsSourceTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index 1455e5b128659..b596de4a4ce4e 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeGeneration; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; @@ -710,7 +711,7 @@ public async Task FormatMetadataAsSource() { var file = await context.GenerateSourceAsync("System.Console", project: context.DefaultProject); var document = context.GetDocument(file); - await Formatting.Formatter.FormatAsync(document); + await Formatter.FormatAsync(document); } }