提交 414fa9d7 编写于 作者: C CyrusNajmabadi

Preserve file banner when generating a type in a new file.

上级 897028e9
......@@ -4782,6 +4782,83 @@ public B()
}",
index: 2);
}
[WorkItem(17361, "https://github.com/dotnet/roslyn/issues/17361")]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)]
public async Task TestPreserveFileBanner1()
{
await TestAddDocumentInRegularAndScriptAsync(
@"// I am a banner
class Program
{
void Main ( )
{
[|Foo|] f ;
}
} ",
@"// I am a banner
internal class Foo
{
}",
expectedContainers: ImmutableArray<string>.Empty,
expectedDocumentName: "Foo.cs",
ignoreTrivia: false);
}
[WorkItem(17361, "https://github.com/dotnet/roslyn/issues/17361")]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)]
public async Task TestPreserveFileBanner2()
{
await TestAddDocumentInRegularAndScriptAsync(
@"/// I am a doc comment
class Program
{
void Main ( )
{
[|Foo|] f ;
}
} ",
@"internal class Foo
{
}",
expectedContainers: ImmutableArray<string>.Empty,
expectedDocumentName: "Foo.cs",
ignoreTrivia: false);
}
[WorkItem(17361, "https://github.com/dotnet/roslyn/issues/17361")]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)]
public async Task TestPreserveFileBanner3()
{
await TestAddDocumentInRegularAndScriptAsync(
@"// I am a banner
using System;
class Program
{
void Main (StackOverflowException e)
{
var f = new [|Foo|](e);
}
}",
@"// I am a banner
using System;
internal class Foo
{
private StackOverflowException e;
public Foo(StackOverflowException e)
{
this.e = e;
}
}",
expectedContainers: ImmutableArray<string>.Empty,
expectedDocumentName: "Foo.cs",
ignoreTrivia: false);
}
}
public partial class GenerateTypeWithUnboundAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
......
......@@ -465,6 +465,75 @@ expectedContainers:=ImmutableArray.Create("Foo"),
expectedDocumentName:="Bar.vb")
End Function
<WorkItem(17361, "https://github.com/dotnet/roslyn/issues/17361")>
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)>
Public Async Function TestPreserveBanner1() As Task
Await TestAddDocumentInRegularAndScriptAsync(
"' I am a banner!
Class Program
Sub Main()
Call New [|Bar|]()
End Sub
End Class",
"' I am a banner!
Friend Class Bar
Public Sub New()
End Sub
End Class
",
expectedContainers:=ImmutableArray(Of String).Empty,
expectedDocumentName:="Bar.vb", ignoreTrivia:=False)
End Function
<WorkItem(17361, "https://github.com/dotnet/roslyn/issues/17361")>
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)>
Public Async Function TestPreserveBanner2() As Task
Await TestAddDocumentInRegularAndScriptAsync(
"''' I am a doc comment!
Class Program
Sub Main()
Call New [|Bar|]()
End Sub
End Class",
"Friend Class Bar
Public Sub New()
End Sub
End Class
",
expectedContainers:=ImmutableArray(Of String).Empty,
expectedDocumentName:="Bar.vb", ignoreTrivia:=False)
End Function
<WorkItem(17361, "https://github.com/dotnet/roslyn/issues/17361")>
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)>
Public Async Function TestPreserveBanner3() As Task
Await TestAddDocumentInRegularAndScriptAsync(
"' I am a banner!
Imports System
Class Program
Sub Main(e As StackOverflowException)
Call New [|Bar|](e)
End Sub
End Class",
"' I am a banner!
Imports System
Friend Class Bar
Private e As StackOverflowException
Public Sub New(e As StackOverflowException)
Me.e = e
End Sub
End Class
",
expectedContainers:=ImmutableArray(Of String).Empty,
expectedDocumentName:="Bar.vb", ignoreTrivia:=False)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)>
Public Async Function TestGenerateIntoGlobalNamespaceNewFile() As Task
Await TestAddDocumentInRegularAndScriptAsync(
......
......@@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.ProjectManagement;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
......@@ -272,10 +273,11 @@ private void AddFoldersToNamespaceContainers(List<string> container, IList<strin
var newSemanticModel = await newDocument.GetSemanticModelAsync(_cancellationToken).ConfigureAwait(false);
var enclosingNamespace = newSemanticModel.GetEnclosingNamespace(0, _cancellationToken);
var namespaceContainersAndUsings = GetNamespaceContainersAndAddUsingsOrImport(isDialog, folders, areFoldersValidIdentifiers, projectToBeUpdated, triggeringProject);
var namespaceContainersAndUsings = GetNamespaceContainersAndAddUsingsOrImport(
isDialog, folders, areFoldersValidIdentifiers, projectToBeUpdated, triggeringProject);
var containers = namespaceContainersAndUsings.Item1;
var includeUsingsOrImports = namespaceContainersAndUsings.Item2;
var containers = namespaceContainersAndUsings.containers;
var includeUsingsOrImports = namespaceContainersAndUsings.usingOrImport;
var rootNamespaceOrType = namedType.GenerateRootNamespaceOrType(containers);
......@@ -293,17 +295,32 @@ private void AddFoldersToNamespaceContainers(List<string> container, IList<strin
// 1: folders -> if triggered from Dialog
// 2: containers -> if triggered not from a Dialog but from QualifiedName
// 3: triggering document folder structure -> if triggered not from a Dialog and a SimpleName
var adjustedContainer = isDialog ? folders :
_state.SimpleName != _state.NameOrMemberAccessExpression ? containers.ToList() : _document.Document.Folders.ToList();
var adjustedContainer = isDialog
? folders
: _state.SimpleName != _state.NameOrMemberAccessExpression
? containers.ToList()
: _document.Document.Folders.ToList();
// Now, take the code that would be generated and actually create an edit that would
// produce a document with that code in it.
var newRoot = await codeGenResult.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);
if (newDocument.Project.Language == _document.Document.Project.Language)
{
var syntaxFacts = _document.Document.GetLanguageService<ISyntaxFactsService>();
var fileBanner = syntaxFacts.GetFileBanner(_document.Root);
if (fileBanner.Any(syntaxFacts.IsRegularComment))
{
newRoot = newRoot.WithPrependedLeadingTrivia(fileBanner);
}
}
return await CreateAddDocumentAndUpdateUsingsOrImportsOperationsAsync(
projectToBeUpdated,
triggeringProject,
documentName,
await codeGenResult.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false),
newRoot,
_document.Document,
includeUsingsOrImports,
adjustedContainer,
......@@ -437,7 +454,7 @@ private async Task<IEnumerable<CodeActionOperation>> GetGenerateIntoContainingNa
return new CodeActionOperation[] { new ApplyChangesOperation(updatedSolution) };
}
private Tuple<string[], string> GetNamespaceContainersAndAddUsingsOrImport(
private (string[] containers, string usingOrImport) GetNamespaceContainersAndAddUsingsOrImport(
bool isDialog,
IList<string> folders,
bool areFoldersValidIdentifiers,
......@@ -467,18 +484,12 @@ private async Task<IEnumerable<CodeActionOperation>> GetGenerateIntoContainingNa
else
{
// Generated from the Dialog
List<string> containerList = new List<string>();
string rootNamespaceOfTheProjectGeneratedInto;
var containerList = new List<string>();
if (_targetProjectChangeInLanguage == TargetProjectChangeInLanguage.NoChange)
{
rootNamespaceOfTheProjectGeneratedInto = _service.GetRootNamespace(_generateTypeOptionsResult.Project.CompilationOptions).Trim();
}
else
{
rootNamespaceOfTheProjectGeneratedInto = _targetLanguageService.GetRootNamespace(_generateTypeOptionsResult.Project.CompilationOptions).Trim();
}
var rootNamespaceOfTheProjectGeneratedInto =
_targetProjectChangeInLanguage == TargetProjectChangeInLanguage.NoChange
? _service.GetRootNamespace(_generateTypeOptionsResult.Project.CompilationOptions).Trim()
: _targetLanguageService.GetRootNamespace(_generateTypeOptionsResult.Project.CompilationOptions).Trim();
var projectManagementService = _document.Project.Solution.Workspace.Services.GetService<IProjectManagementService>();
var defaultNamespace = _generateTypeOptionsResult.DefaultNamespace;
......@@ -526,7 +537,7 @@ private async Task<IEnumerable<CodeActionOperation>> GetGenerateIntoContainingNa
Contract.Assert(includeUsingsOrImports != null);
}
return Tuple.Create(containers, includeUsingsOrImports);
return (containers, includeUsingsOrImports);
}
private async Task<IEnumerable<CodeActionOperation>> GetGenerateIntoTypeOperationsAsync(INamedTypeSymbol namedType)
......
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
......@@ -657,6 +658,17 @@ public static bool IsAnyLambdaOrAnonymousMethod(this SyntaxNode node)
return node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index));
}
public static ImmutableArray<SyntaxTrivia> GetFileBanner(this SyntaxNode root)
{
Debug.Assert(root.FullSpan.Start == 0);
var leadingTrivia = root.GetLeadingTrivia();
var index = 0;
s_fileBannerMatcher.TryMatch(leadingTrivia.ToList(), ref index);
return ImmutableArray.CreateRange(leadingTrivia.Take(index));
}
public static bool IsAnyAssignExpression(this SyntaxNode node)
{
return SyntaxFacts.IsAssignmentExpression(node.Kind());
......
......@@ -1979,5 +1979,8 @@ public bool IsBetweenTypeMembers(SourceText sourceText, SyntaxNode root, int pos
public ImmutableArray<SyntaxNode> GetSelectedMembers(SyntaxNode root, TextSpan textSpan)
=> ImmutableArray<SyntaxNode>.CastUp(root.GetMembersInSpan(textSpan));
public ImmutableArray<SyntaxTrivia> GetFileBanner(SyntaxNode root)
=> root.GetFileBanner();
}
}
\ No newline at end of file
......@@ -279,6 +279,8 @@ internal interface ISyntaxFactsService : ILanguageService
out SyntaxNode newRoot, out SyntaxNode newContextNode);
SyntaxNode GetNextExecutableStatement(SyntaxNode statement);
ImmutableArray<SyntaxTrivia> GetFileBanner(SyntaxNode root);
}
[Flags]
......
' 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.Collections.Immutable
Imports System.Runtime.CompilerServices
Imports System.Threading
Imports Microsoft.CodeAnalysis
......@@ -538,6 +539,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Return DirectCast(node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index)), TSyntaxNode)
End Function
<Extension>
Public Function GetFileBanner(root As SyntaxNode) As ImmutableArray(Of SyntaxTrivia)
Debug.Assert(root.FullSpan.Start = 0)
Dim leadingTrivia = root.GetLeadingTrivia()
Dim index = 0
s_fileBannerMatcher.TryMatch(leadingTrivia.ToList(), index)
Return ImmutableArray.CreateRange(leadingTrivia.Take(index))
End Function
''' <summary>
''' Returns true if this is a block that can contain multiple executable statements. i.e.
''' this node is the VB equivalent of a BlockSyntax in C#.
......
......@@ -1733,5 +1733,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Public Function GetSelectedMembers(root As SyntaxNode, textSpan As TextSpan) As ImmutableArray(Of SyntaxNode) Implements ISyntaxFactsService.GetSelectedMembers
Return ImmutableArray(Of SyntaxNode).CastUp(root.GetMembersInSpan(textSpan))
End Function
Public Function GetFileBanner(root As SyntaxNode) As ImmutableArray(Of SyntaxTrivia) Implements ISyntaxFactsService.GetFileBanner
Return root.GetFileBanner()
End Function
End Class
End Namespace
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册