Skip to content

Commit

Permalink
Merge pull request dotnet#54966 from CyrusNajmabadi/fileScopedNamespa…
Browse files Browse the repository at this point in the history
…cesLineSeparators

Fix 'line separators' with file scoped namespaces
  • Loading branch information
CyrusNajmabadi authored Jul 20, 2021
2 parents 21d77e7 + 48fe6d1 commit 28191ee
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Collections.Generic;
using System.Composition;
Expand Down Expand Up @@ -37,7 +35,7 @@ public async Task<IEnumerable<TextSpan>> GetLineSeparatorsAsync(
TextSpan textSpan,
CancellationToken cancellationToken)
{
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var node = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
var spans = new List<TextSpan>();

Expand All @@ -46,16 +44,14 @@ public async Task<IEnumerable<TextSpan>> GetLineSeparatorsAsync(
foreach (var block in blocks)
{
if (cancellationToken.IsCancellationRequested)
{
return SpecializedCollections.EmptyEnumerable<TextSpan>();
}

switch (block)
{
case TypeDeclarationSyntax typeBlock:
ProcessNodeList(typeBlock.Members, spans, cancellationToken);
continue;
case NamespaceDeclarationSyntax namespaceBlock:
case BaseNamespaceDeclarationSyntax namespaceBlock:
ProcessUsings(namespaceBlock.Usings, spans, cancellationToken);
ProcessNodeList(namespaceBlock.Members, spans, cancellationToken);
continue;
Expand Down Expand Up @@ -97,11 +93,7 @@ private static bool IsSeparableBlock(SyntaxNode node)

/// <summary>Node types that may contain separable blocks.</summary>
private static bool IsSeparableContainer(SyntaxNode node)
{
return node is TypeDeclarationSyntax ||
node is NamespaceDeclarationSyntax ||
node is CompilationUnitSyntax;
}
=> node is TypeDeclarationSyntax or BaseNamespaceDeclarationSyntax or CompilationUnitSyntax;

private static bool IsBadType(SyntaxNode node)
{
Expand Down Expand Up @@ -155,12 +147,10 @@ private static bool IsBadEvent(SyntaxNode node)
private static bool IsBadIndexer(SyntaxNode node)
=> IsBadAccessorList(node as IndexerDeclarationSyntax);

private static bool IsBadAccessorList(BasePropertyDeclarationSyntax baseProperty)
private static bool IsBadAccessorList(BasePropertyDeclarationSyntax? baseProperty)
{
if (baseProperty?.AccessorList == null)
{
return false;
}

return baseProperty.AccessorList.OpenBraceToken.IsMissing ||
baseProperty.AccessorList.CloseBraceToken.IsMissing;
Expand Down
29 changes: 22 additions & 7 deletions src/EditorFeatures/CSharpTest/LineSeparators/LineSeparatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editor.CSharp.LineSeparator;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
Expand Down Expand Up @@ -282,6 +281,20 @@ class C
await AssertTagsOnBracesOrSemicolonsAsync(file, 0, 2);
}

[Fact, Trait(Traits.Feature, Traits.Features.LineSeparators)]
public async Task UsingDirectiveInFileScopedNamespace()
{
var file = @"namespace N;
using System;
class C
{
}
";
await AssertTagsOnBracesOrSemicolonsAsync(file, 1);
}

[Fact, Trait(Traits.Feature, Traits.Features.LineSeparators)]
public async Task PropertyStyleEventDeclaration()
{
Expand Down Expand Up @@ -521,13 +534,15 @@ private static async Task AssertTagsOnBracesOrSemicolonsAsync(string contents, p
await AssertTagsOnBracesOrSemicolonsTokensAsync(contents, tokenIndices, Options.Script);
}

private static async Task AssertTagsOnBracesOrSemicolonsTokensAsync(string contents, int[] tokenIndices, CSharpParseOptions options = null)
private static async Task AssertTagsOnBracesOrSemicolonsTokensAsync(string contents, int[] tokenIndices, CSharpParseOptions? options = null)
{
using var workspace = TestWorkspace.CreateCSharp(contents, options);
var document = workspace.CurrentSolution.GetDocument(workspace.Documents.First().Id);
var lineSeparatorService = Assert.IsType<CSharpLineSeparatorService>(workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService<ILineSeparatorService>());
var spans = await lineSeparatorService.GetLineSeparatorsAsync(document, (await document.GetSyntaxRootAsync()).FullSpan, CancellationToken.None);
var tokens = (await document.GetSyntaxRootAsync(CancellationToken.None)).DescendantTokens().Where(t => t.Kind() == SyntaxKind.CloseBraceToken || t.Kind() == SyntaxKind.SemicolonToken);
var document = workspace.CurrentSolution.GetRequiredDocument(workspace.Documents.First().Id);
var root = await document.GetRequiredSyntaxRootAsync(default);

var lineSeparatorService = Assert.IsType<CSharpLineSeparatorService>(workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService<ILineSeparatorService>());
var spans = await lineSeparatorService.GetLineSeparatorsAsync(document, root.FullSpan, CancellationToken.None);
var tokens = root.DescendantTokens().Where(t => t.Kind() is SyntaxKind.CloseBraceToken or SyntaxKind.SemicolonToken);

Assert.Equal(tokenIndices.Length, spans.Count());

Expand Down

0 comments on commit 28191ee

Please sign in to comment.