diff --git a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs index 1db1098022187..f021e8c203279 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs @@ -8,11 +8,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.BraceCompletion; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editor.Implementation.Formatting; using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; @@ -1585,6 +1583,56 @@ public void FormatArbitraryNodeParenthesizedLambdaExpression() AssertFormatOnArbitraryNode(node, expected); } + [WpfFact] + [WorkItem(57465, "https://github.com/dotnet/roslyn/issues/57465")] + public async Task FormatLambdaWithDirective() + { + var code = @"namespace N +{ + public class C + { + protected void Render() + { + if (outer) + { + M(() => + { +#nullable enable + if (inner) + { + } + } + ); + } + } + } +} +"; + var expected = @"namespace N +{ + public class C + { + protected void Render() + { + if (outer) + { + M(() => + { +#nullable enable + if (inner) + { + } + } + ); + } + } + } +} +"; + + await AssertFormatAsync(expected, code, spans: null); + } + [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] [WpfFact] public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff1() @@ -2246,7 +2294,7 @@ private static void AssertFormatAfterTypeChar(string code, string expected, Dict var newSnapshot = subjectDocument.GetTextBuffer().CurrentSnapshot; - Assert.Equal(expected, newSnapshot.GetText()); + AssertEx.EqualOrDiff(expected, newSnapshot.GetText()); } } } diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index b905addc182d9..7a7eb4a5d95fb 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -5,6 +5,7 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -18,6 +19,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; @@ -176,7 +178,7 @@ protected async Task AssertFormatAsync(string expected, string code, IEnumerable { var factory = (TestFormattingRuleFactoryServiceFactory.Factory)formattingRuleProvider; factory.BaseIndentation = baseIndentation.Value; - factory.TextSpan = spans.First(); + factory.TextSpan = spans?.First() ?? syntaxTree.GetRoot(CancellationToken.None).FullSpan; } var optionSet = workspace.Options; @@ -220,7 +222,7 @@ internal void AssertFormat(Workspace workspace, string expected, SyntaxFormattin if (actual != expected) { _output.WriteLine(actual); - Assert.Equal(expected, actual); + AssertEx.EqualOrDiff(expected, actual); } } @@ -264,12 +266,12 @@ protected async Task AssertFormatWithBaseIndentAsync( string expected, string markupCode, int baseIndentation, Dictionary options = null) { - MarkupTestFile.GetSpan(markupCode, out var code, out var span); + TestFileMarkupParser.GetSpans(markupCode, out var code, out ImmutableArray spans); await AssertFormatAsync( expected, code, - new List { span }, + spans, changedOptionSet: options, baseIndentation: baseIndentation); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs index 50507e48f85ac..d2f26ecb7760b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs @@ -78,7 +78,10 @@ protected override LineColumnRule GetLineColumnRuleBetween(SyntaxTrivia trivia1, if (insertNewLine) { - return LineColumnRule.PreserveLinesWithDefaultIndentation(lines: 0); + if (existingWhitespaceBetween.Spaces != this.Spaces) + return LineColumnRule.PreserveWithGivenSpaces(spaces: this.Spaces); + else + return LineColumnRule.PreserveLinesWithDefaultIndentation(lines: 0); } if (existingWhitespaceBetween.Lines > 0 && existingWhitespaceBetween.Spaces != this.Spaces) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.AnchorData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.AnchorData.cs index a89fb23292cfd..01de97733daff 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.AnchorData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.AnchorData.cs @@ -17,20 +17,21 @@ private class AnchorData { private readonly AnchorIndentationOperation _operation; - public AnchorData(AnchorIndentationOperation operation, int originalColumn) + public AnchorData(AnchorIndentationOperation operation, SyntaxToken anchorToken, int originalColumn) { _operation = operation; + this.AnchorToken = anchorToken; this.OriginalColumn = originalColumn; } public TextSpan TextSpan => _operation.TextSpan; - public SyntaxToken AnchorToken => _operation.AnchorToken; - public SyntaxToken StartToken => _operation.StartToken; public SyntaxToken EndToken => _operation.EndToken; + public SyntaxToken AnchorToken { get; } + public int OriginalColumn { get; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs index b3546b76d42e2..6eb4e32ea9963 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs @@ -403,8 +403,9 @@ public void AddAnchorIndentationOperation(AnchorIndentationOperation operation) return; } - var originalSpace = _tokenStream.GetOriginalColumn(operation.StartToken); - var data = new AnchorData(operation, originalSpace); + var anchorToken = _tokenStream.FirstTokenOfBaseTokenLine(operation.AnchorToken); + var originalSpace = _tokenStream.GetOriginalColumn(anchorToken); + var data = new AnchorData(operation, anchorToken, originalSpace); _anchorTree.AddIntervalInPlace(data); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs index adcfb981e18f6..8175b0dad2b0e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs @@ -13,25 +13,23 @@ namespace Microsoft.CodeAnalysis.Formatting.Rules /// internal sealed class AnchorIndentationOperation { - internal AnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken startToken, SyntaxToken endToken, TextSpan textSpan) + internal AnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken endToken, TextSpan textSpan) { Contract.ThrowIfTrue(anchorToken.RawKind == 0); Contract.ThrowIfTrue(textSpan.Start < 0 || textSpan.Length < 0); - Contract.ThrowIfTrue(startToken.RawKind == 0); Contract.ThrowIfTrue(endToken.RawKind == 0); this.AnchorToken = anchorToken; this.TextSpan = textSpan; - this.StartToken = startToken; this.EndToken = endToken; } public SyntaxToken AnchorToken { get; } public TextSpan TextSpan { get; } - public SyntaxToken StartToken { get; } + public SyntaxToken StartToken => AnchorToken; public SyntaxToken EndToken { get; } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs index c3976ca379f8b..bc025f6b723b8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs @@ -25,16 +25,16 @@ internal static class FormattingOperations /// /// create anchor indentation region around start and end token - /// start token will act as anchor token and right after anchor token to end of end token will become anchor region + /// right after anchor token to end of end token will become anchor region /// - public static AnchorIndentationOperation CreateAnchorIndentationOperation(SyntaxToken startToken, SyntaxToken endToken) - => CreateAnchorIndentationOperation(startToken, startToken, endToken, TextSpan.FromBounds(startToken.Span.End, endToken.Span.End)); + public static AnchorIndentationOperation CreateAnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken endToken) + => CreateAnchorIndentationOperation(anchorToken, endToken, TextSpan.FromBounds(anchorToken.Span.End, endToken.Span.End)); /// /// create anchor indentation region more explicitly by providing all necessary information. /// - public static AnchorIndentationOperation CreateAnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken startToken, SyntaxToken endToken, TextSpan textSpan) - => new(anchorToken, startToken, endToken, textSpan); + public static AnchorIndentationOperation CreateAnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken endToken, TextSpan textSpan) + => new(anchorToken, endToken, textSpan); /// /// create suppress region around start and end token