diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index 903377d6f16d71eaad94813b9cce4321a781ac9a..a7c1bb3b1863d239cb603bf53d62d7bab1186f28 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -1,12 +1,9 @@ // 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.Linq; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Formatting.Rules; -using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Roslyn.Test.Utilities; using Xunit; @@ -2531,6 +2528,29 @@ class UserViewModel expectedIndentation: 25); } + [WorkItem(5495, "https://github.com/dotnet/roslyn/issues/5495")] + [Fact, Trait(Traits.Feature, Traits.Features.SmartIndent)] + public void AfterPartialFromClause() + { + var code = @" +using System.Linq; + +class C +{ + void M() + { + var q = from x + + } +} +"; + + AssertSmartIndent( + code, + indentationLine: 8, + expectedIndentation: 16); + } + private static void AssertSmartIndentInProjection(string markup, int expectedIndentation, CSharpParseOptions options = null) { var optionsSet = options != null diff --git a/src/Workspaces/CSharp/Portable/Formatting/Rules/QueryExpressionFormattingRule.cs b/src/Workspaces/CSharp/Portable/Formatting/Rules/QueryExpressionFormattingRule.cs index e3015f56e464275e5a5347f5f77e69a35d442a86..bd7d144b25a2ba5064b5f910f51c861986eb4694 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/Rules/QueryExpressionFormattingRule.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/Rules/QueryExpressionFormattingRule.cs @@ -26,6 +26,36 @@ public override void AddSuppressOperations(List list, SyntaxN } } + private void AddIndentBlockOperationsForFromClause(List list, FromClauseSyntax fromClause) + { + // Only add the indent block operation if the 'in' keyword is present. Otherwise, we'll get the following: + // + // from x + // in args + // + // Rather than: + // + // from x + // in args + // + // However, we want to get the following result if the 'in' keyword is present to allow nested queries + // to be formatted properly. + // + // from x in + // args + + if (fromClause.InKeyword.IsMissing) + { + return; + } + + var baseToken = fromClause.FromKeyword; + var startToken = fromClause.Expression.GetFirstToken(includeZeroWidth: true); + var endToken = fromClause.Expression.GetLastToken(includeZeroWidth: true); + + AddIndentBlockOperation(list, baseToken, startToken, endToken); + } + public override void AddIndentBlockOperations(List list, SyntaxNode node, OptionSet optionSet, NextAction nextOperation) { nextOperation.Invoke(list); @@ -33,19 +63,15 @@ public override void AddIndentBlockOperations(List list, S var queryExpression = node as QueryExpressionSyntax; if (queryExpression != null) { - var firstToken = queryExpression.FromClause.Expression.GetFirstToken(includeZeroWidth: true); - var lastToken = queryExpression.FromClause.Expression.GetLastToken(includeZeroWidth: true); - AddIndentBlockOperation(list, queryExpression.FromClause.FromKeyword, firstToken, lastToken); + AddIndentBlockOperationsForFromClause(list, queryExpression.FromClause); - for (int i = 0; i < queryExpression.Body.Clauses.Count; i++) + foreach (var queryClause in queryExpression.Body.Clauses) { // if it is nested query expression - var fromClause = queryExpression.Body.Clauses[i] as FromClauseSyntax; + var fromClause = queryClause as FromClauseSyntax; if (fromClause != null) { - firstToken = fromClause.Expression.GetFirstToken(includeZeroWidth: true); - lastToken = fromClause.Expression.GetLastToken(includeZeroWidth: true); - AddIndentBlockOperation(list, fromClause.FromKeyword, firstToken, lastToken); + AddIndentBlockOperationsForFromClause(list, fromClause); } }