提交 8285d42f 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #21087 from CyrusNajmabadi/groupingBetweenImports

Add user facing option to place blank lines between using/import groups.
// 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 System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editing;
......@@ -14,13 +15,19 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Organizing
{
public class OrganizeUsingsTests
{
protected async Task CheckAsync(string initial, string final, bool placeSystemNamespaceFirst = false, CSharpParseOptions options = null)
protected async Task CheckAsync(
string initial, string final,
bool placeSystemNamespaceFirst = false,
bool separateImportGroups = false,
CSharpParseOptions options = null)
{
using (var workspace = TestWorkspace.CreateCSharp(initial))
{
var document = workspace.CurrentSolution.GetDocument(workspace.Documents.First().Id);
workspace.Options = workspace.Options.WithChangedOption(new OptionKey(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language), placeSystemNamespaceFirst);
var newRoot = await (await OrganizeImportsService.OrganizeImportsAsync(document)).GetSyntaxRootAsync();
workspace.Options = workspace.Options.WithChangedOption(new OptionKey(GenerationOptions.SeparateImportDirectiveGroups, document.Project.Language), separateImportGroups);
var newRoot = await (await OrganizeImportsService.OrganizeImportsAsync(document, CancellationToken.None)).GetSyntaxRootAsync();
Assert.Equal(final.NormalizeLineEndings(), newRoot.ToFullString());
}
}
......@@ -1027,5 +1034,84 @@ public async Task CaseSensitivity2()
await CheckAsync(initial, final);
}
[WorkItem(20988, "https://github.com/dotnet/roslyn/issues/20988")]
[Fact, Trait(Traits.Feature, Traits.Features.Organizing)]
public async Task TestGrouping()
{
var initial =
@"// Banner
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
using IntList = System.Collections.Generic.List<int>;
using static System.Console;";
var final =
@"// Banner
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
using static System.Console;
using IntList = System.Collections.Generic.List<int>;
";
await CheckAsync(initial, final, placeSystemNamespaceFirst: true, separateImportGroups: true);
}
[WorkItem(20988, "https://github.com/dotnet/roslyn/issues/20988")]
[Fact, Trait(Traits.Feature, Traits.Features.Organizing)]
public async Task TestGrouping2()
{
// Make sure we don't insert extra newlines if they're already there.
var initial =
@"// Banner
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
using static System.Console;
using IntList = System.Collections.Generic.List<int>;
";
var final =
@"// Banner
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
using static System.Console;
using IntList = System.Collections.Generic.List<int>;
";
await CheckAsync(initial, final, placeSystemNamespaceFirst: true, separateImportGroups: true);
}
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Threading
Imports System.Xml.Linq
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions
......@@ -9,11 +10,15 @@ Imports Microsoft.CodeAnalysis.OrganizeImports
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Organizing
Public Class OrganizeImportsTests
Private Async Function CheckAsync(initial As XElement, final As XElement, Optional placeSystemNamespaceFirst As Boolean = False) As Task
Private Async Function CheckAsync(initial As XElement, final As XElement,
Optional placeSystemNamespaceFirst As Boolean = False,
Optional separateImportGroups As Boolean = False) As Task
Using workspace = TestWorkspace.CreateVisualBasic(initial.NormalizedValue)
Dim document = workspace.CurrentSolution.GetDocument(workspace.Documents.First().Id)
workspace.Options = workspace.Options.WithChangedOption(New OptionKey(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language), placeSystemNamespaceFirst)
Dim newRoot = Await (Await OrganizeImportsService.OrganizeImportsAsync(document)).GetSyntaxRootAsync()
workspace.Options = workspace.Options.WithChangedOption(New OptionKey(GenerationOptions.SeparateImportDirectiveGroups, document.Project.Language), separateImportGroups)
Dim newRoot = Await (Await OrganizeImportsService.OrganizeImportsAsync(document, CancellationToken.None)).GetSyntaxRootAsync()
Assert.Equal(final.NormalizedValue, newRoot.ToFullString())
End Using
End Function
......@@ -678,5 +683,90 @@ Imports ああ
Await CheckAsync(initial, final)
End Function
<WorkItem(20988, "https://github.com/dotnet/roslyn/issues/20988")>
<Fact, Trait(Traits.Feature, Traits.Features.Organizing)>
Public Async Function TestGrouping() As Task
Dim initial =
<content><![CDATA[' Banner
Imports Microsoft.CodeAnalysis.CSharp.Extensions
Imports Microsoft.CodeAnalysis.CSharp.Syntax
Imports System.Collections.Generic
Imports System.Linq
Imports Microsoft.CodeAnalysis.Shared.Extensions
Imports <xmlns:ab="http://NewNamespace">
Imports <xmlns="http://DefaultNamespace">
Imports Roslyn.Utilities
Imports IntList = System.Collections.Generic.List(Of Integer)
Imports <xmlns:zz="http://NextNamespace">
]]></content>
Dim final =
<content><![CDATA[' Banner
Imports System.Collections.Generic
Imports System.Linq
Imports Microsoft.CodeAnalysis.CSharp.Extensions
Imports Microsoft.CodeAnalysis.CSharp.Syntax
Imports Microsoft.CodeAnalysis.Shared.Extensions
Imports Roslyn.Utilities
Imports IntList = System.Collections.Generic.List(Of Integer)
Imports <xmlns:ab="http://NewNamespace">
Imports <xmlns="http://DefaultNamespace">
Imports <xmlns:zz="http://NextNamespace">
]]></content>
Await CheckAsync(initial, final, placeSystemNamespaceFirst:=True, separateImportGroups:=True)
End Function
<WorkItem(20988, "https://github.com/dotnet/roslyn/issues/20988")>
<Fact, Trait(Traits.Feature, Traits.Features.Organizing)>
Public Async Function TestGrouping2() As Task
' Make sure we don't insert extra newlines if they're already there.
Dim initial =
<content><![CDATA[' Banner
Imports System.Collections.Generic
Imports System.Linq
Imports Microsoft.CodeAnalysis.CSharp.Extensions
Imports Microsoft.CodeAnalysis.CSharp.Syntax
Imports Microsoft.CodeAnalysis.Shared.Extensions
Imports Roslyn.Utilities
Imports IntList = System.Collections.Generic.List(Of Integer)
Imports <xmlns:ab="http://NewNamespace">
Imports <xmlns="http://DefaultNamespace">
Imports <xmlns:zz="http://NextNamespace">
]]></content>
Dim final =
<content><![CDATA[' Banner
Imports System.Collections.Generic
Imports System.Linq
Imports Microsoft.CodeAnalysis.CSharp.Extensions
Imports Microsoft.CodeAnalysis.CSharp.Syntax
Imports Microsoft.CodeAnalysis.Shared.Extensions
Imports Roslyn.Utilities
Imports IntList = System.Collections.Generic.List(Of Integer)
Imports <xmlns:ab="http://NewNamespace">
Imports <xmlns="http://DefaultNamespace">
Imports <xmlns:zz="http://NextNamespace">
]]></content>
Await CheckAsync(initial, final, placeSystemNamespaceFirst:=True, separateImportGroups:=True)
End Function
End Class
End Namespace
......@@ -13,18 +13,23 @@ internal partial class CSharpOrganizeImportsService
private class Rewriter : CSharpSyntaxRewriter
{
private readonly bool _placeSystemNamespaceFirst;
private readonly bool _separateGroups;
public readonly IList<TextChange> TextChanges = new List<TextChange>();
public Rewriter(bool placeSystemNamespaceFirst)
public Rewriter(bool placeSystemNamespaceFirst,
bool separateGroups)
{
_placeSystemNamespaceFirst = placeSystemNamespaceFirst;
_separateGroups = separateGroups;
}
public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
{
node = (CompilationUnitSyntax)base.VisitCompilationUnit(node);
UsingsAndExternAliasesOrganizer.Organize(
node.Externs, node.Usings, _placeSystemNamespaceFirst,
node.Externs, node.Usings,
_placeSystemNamespaceFirst, _separateGroups,
out var organizedExternAliasList, out var organizedUsingList);
var result = node.WithExterns(organizedExternAliasList).WithUsings(organizedUsingList);
......@@ -41,7 +46,8 @@ public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax
{
node = (NamespaceDeclarationSyntax)base.VisitNamespaceDeclaration(node);
UsingsAndExternAliasesOrganizer.Organize(
node.Externs, node.Usings, _placeSystemNamespaceFirst,
node.Externs, node.Usings,
_placeSystemNamespaceFirst, _separateGroups,
out var organizedExternAliasList, out var organizedUsingList);
var result = node.WithExterns(organizedExternAliasList).WithUsings(organizedUsingList);
......
......@@ -16,8 +16,11 @@ public async Task<Document> OrganizeImportsAsync(Document document, Cancellation
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
var placeSystemNamespaceFirst = options.GetOption(GenerationOptions.PlaceSystemNamespaceFirst);
var rewriter = new Rewriter(placeSystemNamespaceFirst);
var blankLineBetweenGroups = options.GetOption(GenerationOptions.SeparateImportDirectiveGroups);
var rewriter = new Rewriter(placeSystemNamespaceFirst, blankLineBetweenGroups);
var newRoot = rewriter.Visit(root);
return document.WithSyntaxRoot(newRoot);
......
......@@ -8,9 +8,7 @@ namespace Microsoft.CodeAnalysis.OrganizeImports
{
internal static partial class OrganizeImportsService
{
public static Task<Document> OrganizeImportsAsync(Document document, CancellationToken cancellationToken = default)
{
return document.GetLanguageService<IOrganizeImportsService>().OrganizeImportsAsync(document, cancellationToken);
}
public static Task<Document> OrganizeImportsAsync(Document document, CancellationToken cancellationToken)
=> document.GetLanguageService<IOrganizeImportsService>().OrganizeImportsAsync(document, cancellationToken);
}
}
......@@ -10,15 +10,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.OrganizeImports
Inherits VisualBasicSyntaxRewriter
Private ReadOnly _placeSystemNamespaceFirst As Boolean
Private ReadOnly _separateGroups As Boolean
Public ReadOnly TextChanges As IList(Of TextChange) = New List(Of TextChange)()
Public Sub New(placeSystemNamespaceFirst As Boolean)
Me._placeSystemNamespaceFirst = placeSystemNamespaceFirst
Public Sub New(placeSystemNamespaceFirst As Boolean, separateGroups As Boolean)
_placeSystemNamespaceFirst = placeSystemNamespaceFirst
_separateGroups = separateGroups
End Sub
Public Overrides Function VisitCompilationUnit(node As CompilationUnitSyntax) As SyntaxNode
node = DirectCast(MyBase.VisitCompilationUnit(node), CompilationUnitSyntax)
Dim organizedImports = ImportsOrganizer.Organize(node.Imports, _placeSystemNamespaceFirst)
Dim organizedImports = ImportsOrganizer.Organize(
node.Imports, _placeSystemNamespaceFirst, _separateGroups)
Dim result = node.WithImports(organizedImports)
If result IsNot node Then
......
......@@ -15,8 +15,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.OrganizeImports
cancellationToken As CancellationToken) As Task(Of Document) Implements IOrganizeImportsService.OrganizeImportsAsync
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
Dim options = Await document.GetOptionsAsync(cancellationToken).ConfigureAwait(False)
Dim placeSystemNamespaceFirst = options.GetOption(GenerationOptions.PlaceSystemNamespaceFirst)
Dim rewriter = New Rewriter(placeSystemNamespaceFirst)
Dim separateGroups = options.GetOption(GenerationOptions.SeparateImportDirectiveGroups)
Dim rewriter = New Rewriter(placeSystemNamespaceFirst, separateGroups)
Dim newRoot = rewriter.Visit(root)
Return document.WithSyntaxRoot(newRoot)
End Function
......
......@@ -1059,6 +1059,15 @@ internal class CSharpVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to Separate using directive groups.
/// </summary>
internal static string Separate_using_directive_groups {
get {
return ResourceManager.GetString("Separate_using_directive_groups", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Set other spacing options.
/// </summary>
......
......@@ -525,4 +525,7 @@
<data name="Report_invalid_placeholders_in_string_dot_format_calls" xml:space="preserve">
<value>Report invalid placeholders in 'string.Format' calls</value>
</data>
<data name="Separate_using_directive_groups" xml:space="preserve">
<value>Separate using directive groups</value>
</data>
</root>
\ No newline at end of file
......@@ -27,6 +27,9 @@
<CheckBox x:Name="PlaceSystemNamespaceFirst"
x:Uid="SortUsings_PlaceSystemFirst"
Content="{x:Static local:AdvancedOptionPageStrings.Option_PlaceSystemNamespaceFirst}" />
<CheckBox x:Name="SeparateImportGroups"
x:Uid="SeparateImportGroups"
Content="{x:Static local:AdvancedOptionPageStrings.Option_SeparateImportGroups}" />
<CheckBox x:Name="SuggestForTypesInReferenceAssemblies"
x:Uid="AddImport_SuggestForTypesInReferenceAssemblies"
Content="{x:Static local:AdvancedOptionPageStrings.Option_Suggest_usings_for_types_in_reference_assemblies}" />
......
......@@ -26,6 +26,7 @@ public AdvancedOptionPageControl(IServiceProvider serviceProvider) : base(servic
BindToOption(Perform_editor_feature_analysis_in_external_process, RemoteFeatureOptions.OutOfProcessAllowed);
BindToOption(PlaceSystemNamespaceFirst, GenerationOptions.PlaceSystemNamespaceFirst, LanguageNames.CSharp);
BindToOption(SeparateImportGroups, GenerationOptions.SeparateImportDirectiveGroups, LanguageNames.CSharp);
BindToOption(SuggestForTypesInReferenceAssemblies, SymbolSearchOptions.SuggestForTypesInReferenceAssemblies, LanguageNames.CSharp);
BindToOption(SuggestForTypesInNuGetPackages, SymbolSearchOptions.SuggestForTypesInNuGetPackages, LanguageNames.CSharp);
BindToOption(Split_string_literals_on_enter, SplitStringLiteralOptions.Enabled, LanguageNames.CSharp);
......
......@@ -149,9 +149,10 @@ public static string Option_Performance
}
public static string Option_PlaceSystemNamespaceFirst
{
get { return CSharpVSResources.Place_System_directives_first_when_sorting_usings; }
}
=> CSharpVSResources.Place_System_directives_first_when_sorting_usings;
public static string Option_SeparateImportGroups
=> CSharpVSResources.Separate_using_directive_groups;
public static string Option_Using_Directives =>
CSharpVSResources.Using_Directives;
......
......@@ -460,6 +460,15 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Separate import directive groups.
'''</summary>
Friend Shared ReadOnly Property Separate_import_directive_groups() As String
Get
Return ResourceManager.GetString("Separate_import_directive_groups", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Show completion item _filters.
'''</summary>
......
......@@ -13,7 +13,7 @@
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
......@@ -276,4 +276,7 @@
<data name="Report_invalid_placeholders_in_string_dot_format_calls" xml:space="preserve">
<value>Report invalid placeholders in 'String.Format' calls</value>
</data>
<data name="Separate_import_directive_groups" xml:space="preserve">
<value>Separate import directive groups</value>
</data>
</root>
\ No newline at end of file
......@@ -28,6 +28,9 @@
<CheckBox x:Name="PlaceSystemNamespaceFirst"
x:Uid="SortImports_PlaceSystemFirst"
Content="{x:Static local:AdvancedOptionPageStrings.Option_PlaceSystemNamespaceFirst}" />
<CheckBox x:Name="SeparateImportGroups"
x:Uid="SeparateImportGroups"
Content="{x:Static local:AdvancedOptionPageStrings.Option_SeparateImportGroups}" />
<CheckBox x:Name="SuggestForTypesInReferenceAssemblies"
x:Uid="AddImport_SuggestForTypesInReferenceAssemblies"
Content="{x:Static local:AdvancedOptionPageStrings.Option_Suggest_imports_for_types_in_reference_assemblies}" />
......
......@@ -23,6 +23,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options
BindToOption(Perform_editor_feature_analysis_in_external_process, RemoteFeatureOptions.OutOfProcessAllowed)
BindToOption(PlaceSystemNamespaceFirst, GenerationOptions.PlaceSystemNamespaceFirst, LanguageNames.VisualBasic)
BindToOption(SeparateImportGroups, GenerationOptions.SeparateImportDirectiveGroups, LanguageNames.VisualBasic)
BindToOption(SuggestForTypesInReferenceAssemblies, SymbolSearchOptions.SuggestForTypesInReferenceAssemblies, LanguageNames.VisualBasic)
BindToOption(SuggestForTypesInNuGetPackages, SymbolSearchOptions.SuggestForTypesInNuGetPackages, LanguageNames.VisualBasic)
......
......@@ -191,17 +191,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options
End Get
End Property
Public ReadOnly Property Option_Import_Directives As String
Get
Return BasicVSResources.Import_Directives
End Get
End Property
Public ReadOnly Property Option_Import_Directives As String =
BasicVSResources.Import_Directives
Public ReadOnly Property Option_PlaceSystemNamespaceFirst As String
Get
Return BasicVSResources.Place_System_directives_first_when_sorting_imports
End Get
End Property
Public ReadOnly Property Option_PlaceSystemNamespaceFirst As String =
BasicVSResources.Place_System_directives_first_when_sorting_imports
Public ReadOnly Property Option_SeparateImportGroups As String =
BasicVSResources.Separate_import_directive_groups
Public ReadOnly Property Option_Suggest_imports_for_types_in_reference_assemblies As String
Get
......
......@@ -2,19 +2,93 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Utilities
{
internal static partial class UsingsAndExternAliasesOrganizer
{
private static readonly SyntaxTrivia s_newLine = SyntaxFactory.CarriageReturnLineFeed;
public static void Organize(
SyntaxList<ExternAliasDirectiveSyntax> externAliasList,
SyntaxList<UsingDirectiveSyntax> usingList,
bool placeSystemNamespaceFirst, bool separateGroups,
out SyntaxList<ExternAliasDirectiveSyntax> organizedExternAliasList,
out SyntaxList<UsingDirectiveSyntax> organizedUsingList)
{
OrganizeWorker(
externAliasList, usingList, placeSystemNamespaceFirst,
out organizedExternAliasList, out organizedUsingList);
if (separateGroups)
{
if (organizedExternAliasList.Count > 0 && organizedUsingList.Count > 0)
{
var firstUsing = organizedUsingList[0];
if (!firstUsing.GetLeadingTrivia().Any(t => t.IsEndOfLine()))
{
var newFirstUsing = firstUsing.WithPrependedLeadingTrivia(s_newLine);
organizedUsingList = organizedUsingList.Replace(firstUsing, newFirstUsing);
}
}
for (var i = 1; i < organizedUsingList.Count; i++)
{
var lastUsing = organizedUsingList[i - 1];
var currentUsing = organizedUsingList[i];
if (NeedsGrouping(lastUsing, currentUsing) &&
!currentUsing.GetLeadingTrivia().Any(t => t.IsEndOfLine()))
{
var newCurrentUsing = currentUsing.WithPrependedLeadingTrivia(s_newLine);
organizedUsingList = organizedUsingList.Replace(currentUsing, newCurrentUsing);
}
}
}
}
private static bool NeedsGrouping(
UsingDirectiveSyntax using1,
UsingDirectiveSyntax using2)
{
var directive1IsUsingStatic = using1.StaticKeyword.IsKind(SyntaxKind.StaticKeyword);
var directive2IsUsingStatic = using2.StaticKeyword.IsKind(SyntaxKind.StaticKeyword);
var directive1IsAlias = using1.Alias != null;
var directive2IsAlias = using2.Alias != null;
var directive1IsNamespace = !directive1IsUsingStatic && !directive1IsAlias;
var directive2IsNamespace = !directive2IsUsingStatic && !directive2IsAlias;
if (directive1IsAlias && directive2IsAlias)
{
return false;
}
if (directive1IsUsingStatic && directive2IsUsingStatic)
{
return false;
}
if (directive1IsNamespace && directive2IsNamespace)
{
// Both normal usings. Place them in groups if their first namespace
// component differs.
var name1 = using1.Name.GetFirstToken().ValueText;
var name2 = using2.Name.GetFirstToken().ValueText;
return name1 != name2;
}
// They have different types, definitely put them into new groups.
return true;
}
private static void OrganizeWorker(
SyntaxList<ExternAliasDirectiveSyntax> externAliasList,
SyntaxList<UsingDirectiveSyntax> usingList,
bool placeSystemNamespaceFirst,
......@@ -79,9 +153,7 @@ private static void EnsureNewLines(IList<SyntaxNode> list)
if (!trailingTrivia.Any() || trailingTrivia.Last().Kind() != SyntaxKind.EndOfLineTrivia)
{
// TODO(cyrusn): Don't use CRLF. Use the appropriate
// newline for this file.
list[i] = node.WithTrailingTrivia(trailingTrivia.Concat(SyntaxFactory.CarriageReturnLineFeed));
list[i] = node.WithTrailingTrivia(trailingTrivia.Concat(s_newLine));
}
}
......@@ -90,10 +162,13 @@ private static void EnsureNewLines(IList<SyntaxNode> list)
for (int i = 1; i < list.Count; i++)
{
var node = list[i];
list[i] = node.WithLeadingTrivia(node.GetLeadingTrivia().SkipWhile(t => t.Kind() == SyntaxKind.EndOfLineTrivia));
list[i] = TrimLeadingNewLines(node);
}
list[0] = list[0].WithLeadingTrivia(list[0].GetLeadingTrivia().SkipWhile(t => t.Kind() == SyntaxKind.EndOfLineTrivia));
list[0] = TrimLeadingNewLines(list[0]);
}
private static SyntaxNode TrimLeadingNewLines(SyntaxNode node)
=> node.WithLeadingTrivia(node.GetLeadingTrivia().SkipWhile(t => t.Kind() == SyntaxKind.EndOfLineTrivia));
}
}
......@@ -6,9 +6,16 @@ namespace Microsoft.CodeAnalysis.Editing
{
internal class GenerationOptions
{
public static readonly PerLanguageOption<bool> PlaceSystemNamespaceFirst = new PerLanguageOption<bool>(nameof(GenerationOptions), nameof(PlaceSystemNamespaceFirst), defaultValue: true,
public static readonly PerLanguageOption<bool> PlaceSystemNamespaceFirst = new PerLanguageOption<bool>(nameof(GenerationOptions),
nameof(PlaceSystemNamespaceFirst), defaultValue: true,
storageLocations: new OptionStorageLocation[] {
EditorConfigStorageLocation.ForBoolOption("dotnet_sort_system_directives_first"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PlaceSystemNamespaceFirst")});
public static readonly PerLanguageOption<bool> SeparateImportDirectiveGroups = new PerLanguageOption<bool>(
nameof(GenerationOptions), nameof(SeparateImportDirectiveGroups), defaultValue: false,
storageLocations: new OptionStorageLocation[] {
EditorConfigStorageLocation.ForBoolOption("dotnet_separate_import_directive_groups"),
new RoamingProfileStorageLocation($"TextEditor.%LANGUAGE%.Specific.{nameof(SeparateImportDirectiveGroups)}")});
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System
Imports System.Collections.Generic
Imports System.Collections.Immutable
Imports System.Globalization
Imports System.Linq
Imports System.Text
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Shared.Collections
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.Utilities
Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities
Partial Friend Class ImportsOrganizer
Private Shared ReadOnly s_newLine As SyntaxTrivia = SyntaxFactory.CarriageReturnLineFeed
Public Shared Function Organize([imports] As SyntaxList(Of ImportsStatementSyntax),
placeSystemNamespaceFirst As Boolean) As SyntaxList(Of ImportsStatementSyntax)
placeSystemNamespaceFirst As Boolean,
separateGroups As Boolean) As SyntaxList(Of ImportsStatementSyntax)
[imports] = OrganizeWorker([imports], placeSystemNamespaceFirst)
If separateGroups Then
For i = 1 To [imports].Count - 1
Dim lastImport = [imports](i - 1)
Dim currentImport = [imports](i)
If NeedsGrouping(lastImport, currentImport) AndAlso
Not currentImport.GetLeadingTrivia().Any(Function(t) t.IsEndOfLine()) Then
[imports] = [imports].Replace(
currentImport, currentImport.WithPrependedLeadingTrivia(s_newLine))
End If
Next
End If
Return [imports]
End Function
Private Shared Function NeedsGrouping(import1 As ImportsStatementSyntax,
import2 As ImportsStatementSyntax) As Boolean
If import1.ImportsClauses.Count = 0 OrElse import2.ImportsClauses.Count = 0 Then
Return False
End If
Dim importClause1 = import1.ImportsClauses(0)
Dim importClause2 = import2.ImportsClauses(0)
Dim simpleClause1 = TryCast(importClause1, SimpleImportsClauseSyntax)
Dim simpleClause2 = TryCast(importClause2, SimpleImportsClauseSyntax)
If simpleClause1 Is Nothing AndAlso simpleClause2 Is Nothing Then
Return False
End If
If simpleClause1 IsNot Nothing AndAlso simpleClause2 IsNot Nothing Then
Dim isAlias1 = simpleClause1.Alias IsNot Nothing
Dim isAlias2 = simpleClause2.Alias IsNot Nothing
If isAlias1 AndAlso isAlias2 Then
Return False
End If
If Not isAlias1 AndAlso Not isAlias2 Then
' named imports
Dim name1 = simpleClause1.Name.GetFirstToken().ValueText
Dim name2 = simpleClause2.Name.GetFirstToken().ValueText
Return Not VisualBasicSyntaxFactsService.Instance.StringComparer.Equals(name1, name2)
End If
End If
' Different kinds of imports. Definitely place into separate groups.
Return True
End Function
Public Shared Function OrganizeWorker([imports] As SyntaxList(Of ImportsStatementSyntax),
placeSystemNamespaceFirst As Boolean) As SyntaxList(Of ImportsStatementSyntax)
If [imports].Count > 1 Then
Dim initialList = New List(Of ImportsStatementSyntax)([imports])
If Not [imports].SpansPreprocessorDirective() Then
......@@ -51,7 +100,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities
Private Shared Sub EnsureNewLines(list As List(Of ImportsStatementSyntax))
Dim endOfLine = GetExistingEndOfLineTrivia(list)
endOfLine = If(endOfLine.Kind = SyntaxKind.None, SyntaxFactory.CarriageReturnLineFeed, endOfLine)
endOfLine = If(endOfLine.Kind = SyntaxKind.None, s_newLine, endOfLine)
For i = 0 To list.Count - 1
If Not list(i).GetTrailingTrivia().Any(SyntaxKind.EndOfLineTrivia) Then
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册