Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
0f9a0009
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
0f9a0009
编写于
11月 29, 2018
作者:
G
Gen Lu
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Some minor changes
上级
62058afd
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
186 addition
and
190 deletion
+186
-190
src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/CSharpSyncNamespaceTestsBase.cs
...CodeActions/SyncNamespace/CSharpSyncNamespaceTestsBase.cs
+0
-1
src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs
...efactorings/SyncNamespace/CSharpChangeNamespaceService.cs
+72
-70
src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs
...actorings/SyncNamespace/AbstractChangeNamespaceService.cs
+107
-112
src/Features/Core/Portable/CodeRefactorings/SyncNamespace/IChangeNamespaceService.cs
...CodeRefactorings/SyncNamespace/IChangeNamespaceService.cs
+4
-4
src/Features/VisualBasic/Portable/CodeRefactorings/SyncNamespace/VisualBasicChangeNamespaceService.vb
...orings/SyncNamespace/VisualBasicChangeNamespaceService.vb
+3
-3
未找到文件。
src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/CSharpSyncNamespaceTestsBase.cs
浏览文件 @
0f9a0009
...
...
@@ -91,7 +91,6 @@ protected async Task TestMoveFileToMatchNamespace(string initialMarkup, List<str
}
Assert
.
True
(
expectedFolderPaths
.
Count
()
==
actualFolderPaths
.
Count
,
"Number of available \"Move file\" actions are not equal."
);
foreach
(
var
expected
in
expectedFolderPaths
)
{
Assert
.
True
(
actualFolderPaths
.
Contains
(
expected
));
...
...
src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs
浏览文件 @
0f9a0009
...
...
@@ -192,7 +192,7 @@ protected override SyntaxList<MemberDeclarationSyntax> GetMemberDeclarationsInCo
}
/// <summary>
/// Try to change the namespace declaration based on the follwing rules:
/// Try to change the namespace declaration based on the foll
o
wing rules:
/// - if neither declared nor target namespace are "" (i.e. global namespace),
/// then we try to change the name of the namespace.
/// - if declared namespace is "", then we try to move all types declared
...
...
@@ -206,11 +206,12 @@ protected override SyntaxList<MemberDeclarationSyntax> GetMemberDeclarationsInCo
ImmutableArray
<
string
>
targetNamespaceParts
)
{
Debug
.
Assert
(!
declaredNamespaceParts
.
IsDefault
&&
!
targetNamespaceParts
.
IsDefault
);
var
container
=
root
.
GetAnnotatedNodes
(
s_c
ontainerAnnotation
[
0
]).
Single
();
var
container
=
root
.
GetAnnotatedNodes
(
C
ontainerAnnotation
[
0
]).
Single
();
// Move everything from global namespace to a namespace declaration
if
(
container
is
CompilationUnitSyntax
compilationUnit
)
{
Debug
.
Assert
(!
compilationUnit
.
Members
.
Any
(
m
=>
m
is
NamespaceDeclarationSyntax
));
Debug
.
Assert
(
IsGlobalNamespace
(
declaredNamespaceParts
));
var
targetNamespaceDecl
=
SyntaxFactory
.
NamespaceDeclaration
(
...
...
@@ -220,7 +221,7 @@ protected override SyntaxList<MemberDeclarationSyntax> GetMemberDeclarationsInCo
usings
:
default
,
members
:
compilationUnit
.
Members
);
return
compilationUnit
.
WithMembers
(
new
SyntaxList
<
MemberDeclarationSyntax
>(
targetNamespaceDecl
))
.
WithoutAnnotations
(
s_c
ontainerAnnotation
);
.
WithoutAnnotations
(
C
ontainerAnnotation
);
}
if
(
container
is
NamespaceDeclarationSyntax
namespaceDecl
)
...
...
@@ -278,12 +279,78 @@ protected override SyntaxList<MemberDeclarationSyntax> GetMemberDeclarationsInCo
namespaceDecl
.
WithName
(
CreateNameSyntax
(
targetNamespaceParts
,
aliasQualifier
:
null
,
targetNamespaceParts
.
Length
-
1
)
.
WithTriviaFrom
(
namespaceDecl
.
Name
).
WithAdditionalAnnotations
(
WarningAnnotation
))
.
WithoutAnnotations
(
s_c
ontainerAnnotation
));
.
WithoutAnnotations
(
C
ontainerAnnotation
));
}
throw
ExceptionUtilities
.
Unreachable
;
}
/// <summary>
/// For the node specified by <paramref name="span"/> to be applicable container, it must be a namespace
/// declaration or a compilation unit, contain no partial declarations and meet the following additional
/// requirements:
///
/// - If a namespace declaration:
/// 1. It doesn't contain or is nested in other namespace declarations
/// 2. The name of the namespace is valid (i.e. no errors)
///
/// - If a compilation unit (i.e. <paramref name="span"/> is empty), there must be no namespace declaration
/// inside (i.e. all members are declared in global namespace)
/// </summary>
protected
override
async
Task
<
SyntaxNode
>
TryGetApplicableContainerFromSpanAsync
(
Document
document
,
TextSpan
span
,
CancellationToken
cancellationToken
)
{
var
compilationUnit
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
)
as
CompilationUnitSyntax
;
SyntaxNode
container
=
null
;
// Empty span means that user wants to move all types declared in the document to a new namespace.
// This action is only supported when everything in the document is declared in global namespace,
// which we use the number of namespace declaration nodes to decide.
if
(
span
.
IsEmpty
)
{
var
hasNamespaceDecl
=
compilationUnit
.
DescendantNodes
(
IsCompilationUnitOrNamespaceDeclaration
)
.
OfType
<
NamespaceDeclarationSyntax
>().
Any
();
if
(
hasNamespaceDecl
)
{
return
null
;
}
container
=
compilationUnit
;
}
else
{
// Otherwise, the span should contain a namespace declaration node, which must be the only one
// in the entire syntax spine to enable the change namespace operation.
var
node
=
compilationUnit
.
FindNode
(
span
,
getInnermostNodeForTie
:
true
);
var
namespaceDecl
=
node
.
AncestorsAndSelf
().
OfType
<
NamespaceDeclarationSyntax
>().
SingleOrDefault
();
if
(
namespaceDecl
?.
Name
.
GetDiagnostics
().
All
(
diag
=>
diag
.
DefaultSeverity
!=
DiagnosticSeverity
.
Error
)
!=
true
)
{
return
null
;
}
if
(
node
.
DescendantNodes
(
IsCompilationUnitOrNamespaceDeclaration
).
OfType
<
NamespaceDeclarationSyntax
>().
Any
())
{
return
null
;
}
container
=
namespaceDecl
;
}
var
containsPartial
=
await
ContainsPartialTypeWithMultipleDeclarationsAsync
(
document
,
container
,
cancellationToken
).
ConfigureAwait
(
false
);
if
(
containsPartial
)
{
return
null
;
}
return
container
;
bool
IsCompilationUnitOrNamespaceDeclaration
(
SyntaxNode
n
)
=>
n
is
CompilationUnitSyntax
||
n
is
NamespaceDeclarationSyntax
;
}
private
static
bool
IsGlobalNamespace
(
ImmutableArray
<
string
>
parts
)
=>
parts
.
Length
==
1
&&
parts
[
0
].
Length
==
0
;
...
...
@@ -326,7 +393,7 @@ private NameSyntax CreateNameSyntax(ImmutableArray<string> namespaceParts, strin
/// Leading trivia of the node and trivia around opening brace, as well as
/// trivia around closing brace are concatenated together respectively.
/// </summary>
private
static
(
ImmutableArray
<
SyntaxTrivia
>
openingTrivia
,
ImmutableArray
<
SyntaxTrivia
>
closingTrivia
)
private
static
(
ImmutableArray
<
SyntaxTrivia
>
openingTrivia
,
ImmutableArray
<
SyntaxTrivia
>
closingTrivia
)
GetOpeningAndClosingTriviaOfNamespaceDeclaration
(
NamespaceDeclarationSyntax
namespaceDeclaration
)
{
var
openingBuilder
=
ArrayBuilder
<
SyntaxTrivia
>.
GetInstance
();
...
...
@@ -340,70 +407,5 @@ private static (ImmutableArray<SyntaxTrivia> openingTrivia, ImmutableArray<Synta
return
(
openingBuilder
.
ToImmutableAndFree
(),
closingBuilder
.
ToImmutableAndFree
());
}
protected
override
async
Task
<
SyntaxNode
>
TryGetApplicableContainerFromSpanAsync
(
Document
document
,
TextSpan
span
,
CancellationToken
cancellationToken
)
{
var
container
=
await
TryGetApplicableContainerWorkerAsync
(
document
,
span
,
cancellationToken
);
if
(
container
!=
null
)
{
var
containsPartial
=
await
ContainsPartialTypeWithMultipleDeclarationsAsync
(
document
,
container
,
cancellationToken
)
.
ConfigureAwait
(
false
);
if
(!
containsPartial
)
{
return
container
;
}
}
return
default
;
}
/// <summary>
/// To be applicable, the node specified by <paramref name="span"/> must be a namespace declaration or
/// a compilation unit, and meet the following requirements:
///
/// - If a namespace declaration:
/// 1. It doesn't contain or is nested in other namespace declarations
/// 2. The name of the namespace is valid (i.e. no errors)
///
/// - If a compilation unit, there must be no namespace declaration and all types in the document are
/// declared in global namespace
/// </summary>
private
static
async
Task
<
SyntaxNode
>
TryGetApplicableContainerWorkerAsync
(
Document
document
,
TextSpan
span
,
CancellationToken
cancellationToken
)
{
var
compilationUnit
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
)
as
CompilationUnitSyntax
;
// Empty span means that user wants to move all types declared in the document to a new namespace.
// This action is only supported when everything in the document is declared in global namespace,
// which we use the number of namespace declaration nodes as the indication.
if
(
span
.
IsEmpty
)
{
var
hasNamespaceDecl
=
compilationUnit
.
DescendantNodes
(
IsCompilationUnitOrNamespaceDeclaration
)
.
OfType
<
NamespaceDeclarationSyntax
>().
Any
();
return
hasNamespaceDecl
?
null
:
compilationUnit
;
}
// Otherwise, the span should contain a namespace declaration node, which must be the only one
// in the entire syntax spine to enable the change namespace operation.
var
node
=
compilationUnit
.
FindNode
(
span
,
getInnermostNodeForTie
:
true
);
var
namespaceDecl
=
node
.
AncestorsAndSelf
().
OfType
<
NamespaceDeclarationSyntax
>().
SingleOrDefault
();
if
(
namespaceDecl
?.
Name
.
GetDiagnostics
().
All
(
diag
=>
diag
.
DefaultSeverity
!=
DiagnosticSeverity
.
Error
)
!=
true
)
{
return
null
;
}
if
(
node
.
DescendantNodes
(
IsCompilationUnitOrNamespaceDeclaration
)
.
OfType
<
NamespaceDeclarationSyntax
>().
Any
())
{
return
null
;
}
return
namespaceDecl
;
bool
IsCompilationUnitOrNamespaceDeclaration
(
SyntaxNode
n
)
=>
n
is
CompilationUnitSyntax
||
n
is
NamespaceDeclarationSyntax
;
}
}
}
src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs
浏览文件 @
0f9a0009
...
...
@@ -25,9 +25,9 @@ namespace Microsoft.CodeAnalysis.ChangeNamespace
/// </summary>
internal
abstract
class
AbstractChangeNamespaceService
:
IChangeNamespaceService
{
public
abstract
Task
<
bool
>
CanChangeNamespaceAsync
(
Document
document
,
SyntaxNode
node
,
CancellationToken
cancellationToken
);
public
abstract
Task
<
bool
>
CanChangeNamespaceAsync
(
Document
document
,
SyntaxNode
container
,
CancellationToken
cancellationToken
);
public
abstract
Task
<
Solution
>
ChangeNamespaceAsync
(
Document
document
,
SyntaxNode
node
,
string
targetNamespace
,
CancellationToken
cancellationToken
);
public
abstract
Task
<
Solution
>
ChangeNamespaceAsync
(
Document
document
,
SyntaxNode
container
,
string
targetNamespace
,
CancellationToken
cancellationToken
);
/// <summary>
/// Try to get a new node to replace given node, which is a reference to a top-level type declared inside the
...
...
@@ -53,9 +53,9 @@ internal abstract class AbstractChangeNamespaceService<TNamespaceDeclarationSynt
private
static
readonly
char
[]
s_dotSeparator
=
new
[]
{
'.'
};
/// <summary>
/// The annotation used to
mar
k applicable container in each document to be fixed.
/// The annotation used to
trac
k applicable container in each document to be fixed.
/// </summary>
protected
static
SyntaxAnnotation
[]
s_c
ontainerAnnotation
{
get
;
}
=
new
[]
{
new
SyntaxAnnotation
()
};
protected
static
SyntaxAnnotation
[]
C
ontainerAnnotation
{
get
;
}
=
new
[]
{
new
SyntaxAnnotation
()
};
protected
static
SyntaxAnnotation
WarningAnnotation
{
get
;
}
=
CodeActions
.
WarningAnnotation
.
Create
(
...
...
@@ -68,23 +68,116 @@ internal abstract class AbstractChangeNamespaceService<TNamespaceDeclarationSynt
protected
abstract
Task
<
SyntaxNode
>
TryGetApplicableContainerFromSpanAsync
(
Document
document
,
TextSpan
span
,
CancellationToken
cancellationToken
);
protected
abstract
string
GetDeclaredNamespace
(
SyntaxNode
node
);
protected
abstract
string
GetDeclaredNamespace
(
SyntaxNode
container
);
protected
abstract
Task
<
ImmutableArray
<(
DocumentId
id
,
SyntaxNode
container
)>>
CanChangeNamespaceWorkerAsync
(
Document
document
,
SyntaxNode
node
,
CancellationToken
cancellationToken
);
protected
abstract
Task
<
ImmutableArray
<(
DocumentId
id
,
SyntaxNode
container
)>>
CanChangeNamespaceWorkerAsync
(
Document
document
,
SyntaxNode
container
,
CancellationToken
cancellationToken
);
public
override
async
Task
<
bool
>
CanChangeNamespaceAsync
(
Document
document
,
SyntaxNode
node
,
CancellationToken
cancellationToken
)
public
override
async
Task
<
bool
>
CanChangeNamespaceAsync
(
Document
document
,
SyntaxNode
container
,
CancellationToken
cancellationToken
)
{
var
applicableContainers
=
await
CanChangeNamespaceWorkerAsync
(
document
,
node
,
cancellationToken
).
ConfigureAwait
(
false
);
var
applicableContainers
=
await
CanChangeNamespaceWorkerAsync
(
document
,
container
,
cancellationToken
).
ConfigureAwait
(
false
);
return
!
applicableContainers
.
IsDefault
;
}
public
override
async
Task
<
Solution
>
ChangeNamespaceAsync
(
Document
document
,
SyntaxNode
container
,
string
targetNamespace
,
CancellationToken
cancellationToken
)
{
var
solution
=
document
.
Project
.
Solution
;
// Make sure given namespace name is valid, "" means global namespace.
var
syntaxFacts
=
document
.
GetLanguageService
<
ISyntaxFactsService
>();
if
(
targetNamespace
==
null
||
(
targetNamespace
.
Length
>
0
&&
!
targetNamespace
.
Split
(
s_dotSeparator
).
All
(
syntaxFacts
.
IsValidIdentifier
)))
{
return
solution
;
}
var
containersFromAllDocuments
=
await
CanChangeNamespaceWorkerAsync
(
document
,
container
,
cancellationToken
).
ConfigureAwait
(
false
);
if
(
containersFromAllDocuments
.
IsDefault
)
{
return
solution
;
}
// No action required if declared namespace already matches target.
var
declaredNamespace
=
GetDeclaredNamespace
(
container
);
if
(
syntaxFacts
.
StringComparer
.
Equals
(
targetNamespace
,
declaredNamespace
))
{
return
solution
;
}
var
annotatedSolution
=
await
AnnotateContainersAsync
(
solution
,
containersFromAllDocuments
,
cancellationToken
).
ConfigureAwait
(
false
);
// Here's the entire process for changing namespace:
// 1. Change the namespace declaration, fix references and add imports that might be necessary.
// 2. Explicitly merge the diff to get a new solution.
// 3. Remove added imports that are unnecessary.
// 4. Do another explicit diff merge based on last merged solution.
//
// The reason for doing explicit diff merge twice is so merging after remove unnecessary imports can be correctly handled.
var
ids
=
containersFromAllDocuments
.
SelectAsArray
(
pair
=>
pair
.
id
);
var
solutionAfterNamespaceChange
=
annotatedSolution
;
var
referenceDocuments
=
PooledHashSet
<
DocumentId
>.
GetInstance
();
try
{
foreach
(
var
id
in
ids
)
{
var
(
newSolution
,
refDocumentIds
)
=
await
ChangeNamespaceInSingleDocumentAsync
(
solutionAfterNamespaceChange
,
id
,
declaredNamespace
,
targetNamespace
,
cancellationToken
)
.
ConfigureAwait
(
false
);
solutionAfterNamespaceChange
=
newSolution
;
referenceDocuments
.
AddRange
(
refDocumentIds
);
}
var
solutionAfterFirstMerge
=
await
MergeDiffAsync
(
solution
,
solutionAfterNamespaceChange
,
cancellationToken
).
ConfigureAwait
(
false
);
// After changing documents, we still need to remove unnecessary imports related to our change.
// We don't try to remove all imports that might become unnecessary/invalid after the namespace change,
// just ones that fully matche the old/new namespace. Because it's hard to get it right and will almost
// certainly cause perf issue.
// For example, if we are changing namespace `Foo.Bar` (which is the only namespace declaration with such name)
// to `A.B`, the using of name `Bar` in a different file below would remain untouched, even it's no longer valid:
//
// namespace Foo
// {
// using Bar;
// ~~~~~~~~~
// }
//
// Also, because we may have added different imports to document that triggered the refactoring
// and the documents that reference affected types declared in changed namespace, we try to remove
// unnecessary imports separately.
var
solutionAfterImportsRemoved
=
await
RemoveUnnecessaryImportsAsync
(
solutionAfterFirstMerge
,
containersFromAllDocuments
.
SelectAsArray
(
pair
=>
pair
.
id
),
CreateAllContainingNamespaces
(
declaredNamespace
),
cancellationToken
).
ConfigureAwait
(
false
);
solutionAfterImportsRemoved
=
await
RemoveUnnecessaryImportsAsync
(
solutionAfterImportsRemoved
,
referenceDocuments
.
ToImmutableArray
(),
ImmutableArray
.
Create
(
declaredNamespace
,
targetNamespace
),
cancellationToken
).
ConfigureAwait
(
false
);
return
await
MergeDiffAsync
(
solutionAfterFirstMerge
,
solutionAfterImportsRemoved
,
cancellationToken
).
ConfigureAwait
(
false
);
}
finally
{
referenceDocuments
.
Free
();
}
}
protected
async
Task
<
ImmutableArray
<(
DocumentId
,
SyntaxNode
)>>
TryGetApplicableContainersFromAllDocumentsAsync
(
Solution
solution
,
ImmutableArray
<
DocumentId
>
ids
,
TextSpan
span
,
CancellationToken
cancellationToken
)
{
// If the node s
elected doesn't meet the requirement for changing namespace
in any of the documents
// If the node s
pecified by span doesn't meet the requirement to be an applicable container
in any of the documents
// (See `TryGetApplicableContainerFromSpanAsync`), or we are getting different namespace declarations among
// those documents, then we know we can't make a proper code change. We will return null and the check
// will return false. We use span of namespace declaration found in each document to decide if they are identical.
...
...
@@ -125,13 +218,16 @@ public override async Task<bool> CanChangeNamespaceAsync(Document document, Synt
}
}
/// <summary>
/// Mark container nodes with our annotation so we can keep track of them across syntax modifications.
/// </summary>
protected
async
Task
<
Solution
>
AnnotateContainersAsync
(
Solution
solution
,
ImmutableArray
<(
DocumentId
,
SyntaxNode
)>
containers
,
CancellationToken
cancellationToken
)
{
var
solutionEditor
=
new
SolutionEditor
(
solution
);
foreach
(
var
(
id
,
container
)
in
containers
)
{
var
documentEditor
=
await
solutionEditor
.
GetDocumentEditorAsync
(
id
,
cancellationToken
).
ConfigureAwait
(
false
);
documentEditor
.
ReplaceNode
(
container
,
container
.
WithAdditionalAnnotations
(
s_c
ontainerAnnotation
));
documentEditor
.
ReplaceNode
(
container
,
container
.
WithAdditionalAnnotations
(
C
ontainerAnnotation
));
}
return
solutionEditor
.
GetChangedSolution
();
}
...
...
@@ -178,107 +274,6 @@ protected static bool IsSupportedLinkedDocument(Document document, out Immutable
return
true
;
}
/// <summary>
/// This code action tries to change the name of the namespace declaration to
/// match the folder hierarchy of the document. The new namespace is constructed
/// by concatenating the default namespace of the project and all the folders in
/// the file path up to the project root.
///
/// For example, if he default namespace is `A.B.C`, file path is
/// "[project root dir]\D\E\F\Class1.cs" and declared namespace in the file is
/// `Foo.Bar.Baz`, then this action will change the namespace declaration
/// to `A.B.C.D.E.F`.
///
/// Note that it also handles the case where the target namespace or declared namespace
/// is global namespace, i.e. default namespace is "" and the file is located at project
/// root directory, and no namespace declaration in the document, respectively.
/// </summary>
public
override
async
Task
<
Solution
>
ChangeNamespaceAsync
(
Document
document
,
SyntaxNode
container
,
string
targetNamespace
,
CancellationToken
cancellationToken
)
{
var
solution
=
document
.
Project
.
Solution
;
var
syntaxFacts
=
document
.
GetLanguageService
<
ISyntaxFactsService
>();
if
(
targetNamespace
==
null
||
(
targetNamespace
.
Length
>
0
&&
!
targetNamespace
.
Split
(
s_dotSeparator
).
All
(
syntaxFacts
.
IsValidIdentifier
)))
{
return
solution
;
}
var
containersFromAllDocuments
=
await
CanChangeNamespaceWorkerAsync
(
document
,
container
,
cancellationToken
).
ConfigureAwait
(
false
);
if
(
containersFromAllDocuments
.
IsDefault
)
{
return
solution
;
}
var
annotatedSolution
=
await
AnnotateContainersAsync
(
solution
,
containersFromAllDocuments
,
cancellationToken
).
ConfigureAwait
(
false
);
// Here's the entire process for changing namespace:
// 1. Change the namespace declaration, fix references and add imports that might be necessary.
// 2. Explicitly merge the diff to get a new solution.
// 3. Remove added imports that are unnecessary.
// 4. Do another explicit diff merge based on last merged solution.
//
// The reason for doing explicit diff merge twice is so merging after remove unnecessary imports can be correctly handled.
var
ids
=
containersFromAllDocuments
.
SelectAsArray
(
pair
=>
pair
.
id
);
var
declaredNamespace
=
GetDeclaredNamespace
(
container
);
var
solutionAfterNamespaceChange
=
annotatedSolution
;
var
referenceDocuments
=
PooledHashSet
<
DocumentId
>.
GetInstance
();
try
{
foreach
(
var
id
in
ids
)
{
var
(
newSolution
,
refDocumentIds
)
=
await
ChangeNamespaceInSingleDocumentAsync
(
solutionAfterNamespaceChange
,
id
,
declaredNamespace
,
targetNamespace
,
cancellationToken
)
.
ConfigureAwait
(
false
);
solutionAfterNamespaceChange
=
newSolution
;
referenceDocuments
.
AddRange
(
refDocumentIds
);
}
var
solutionAfterFirstMerge
=
await
MergeDiffAsync
(
solution
,
solutionAfterNamespaceChange
,
cancellationToken
).
ConfigureAwait
(
false
);
// After changing documents, we still need to remove unnecessary imports related to our change.
// We don't try to remove all imports that might become unnecessary/invalid after the namespace change,
// just ones that fully matche the old/new namespace. Because it's hard to get it right and will almost
// certainly cause perf issue.
// For example, if we are changing namespace `Foo.Bar` (which is the only namespace declaration with such name)
// to `A.B`, the using of name `Bar` in a different file below would remain untouched, even it's no longer valid:
//
// namespace Foo
// {
// using Bar;
// ~~~~~~~~~
// }
//
// Also, because we may have added different imports to document that triggered the refactoring
// and the documents that reference affected types declared in changed namespace, we try to remove
// unnecessary imports separately.
var
solutionAfterImportsRemoved
=
await
RemoveUnnecessaryImportsAsync
(
solutionAfterFirstMerge
,
containersFromAllDocuments
.
SelectAsArray
(
pair
=>
pair
.
id
),
CreateAllContainingNamespaces
(
declaredNamespace
),
cancellationToken
).
ConfigureAwait
(
false
);
solutionAfterImportsRemoved
=
await
RemoveUnnecessaryImportsAsync
(
solutionAfterImportsRemoved
,
referenceDocuments
.
ToImmutableArray
(),
ImmutableArray
.
Create
(
declaredNamespace
,
targetNamespace
),
cancellationToken
).
ConfigureAwait
(
false
);
return
await
MergeDiffAsync
(
solutionAfterFirstMerge
,
solutionAfterImportsRemoved
,
cancellationToken
).
ConfigureAwait
(
false
);
}
finally
{
referenceDocuments
.
Free
();
}
}
private
async
Task
<
ImmutableArray
<
ISymbol
>>
GetDeclaredSymbolsInContainerAsync
(
Document
document
,
SyntaxNode
container
,
...
...
@@ -350,7 +345,7 @@ private static SyntaxNode CreateImport(SyntaxGenerator syntaxGenerator, string n
{
var
document
=
solution
.
GetDocument
(
id
);
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
container
=
root
.
GetAnnotatedNodes
(
s_c
ontainerAnnotation
[
0
]).
Single
();
var
container
=
root
.
GetAnnotatedNodes
(
C
ontainerAnnotation
[
0
]).
Single
();
// Get types declared in the changing namespace, because we need to fix all references to them,
// e.g. change the namespace for qualified name, add imports to proper containers, etc.
...
...
src/Features/Core/Portable/CodeRefactorings/SyncNamespace/IChangeNamespaceService.cs
浏览文件 @
0f9a0009
...
...
@@ -11,17 +11,17 @@ internal interface IChangeNamespaceService : ILanguageService
/// <summary>
/// Determine whether we can change the namespace for given <paramref name="container"/> in the document.
/// Linked documents are not supported, except for a regular document in a multi-targeting project,
/// where the container node must be consisten among all linked documents.
/// where the container node must be consisten
t
among all linked documents.
/// Here's the additional requirements on <paramref name="container"/> to use this service:
///
/// - If <paramref name="container"/> is a namespace declaration node:
/// 1.
d
oesn't contain or is nested in other namespace declarations
/// 1.
D
oesn't contain or is nested in other namespace declarations
/// 2. The name of the namespace is valid (i.e. no errors)
/// 3. No partial type declared in the namespace. Otherwise its multiple declaration will
/// end up in different namespace.
///
/// - If <paramref name="container"/> is a compilation unit node:
/// 1.
i
t must contain no namespace declaration
/// 1.
I
t must contain no namespace declaration
/// 2. No partial type declared in the document. Otherwise its multiple declaration will
/// end up in different namespace.
///
...
...
@@ -33,7 +33,7 @@ internal interface IChangeNamespaceService : ILanguageService
/// Change namespace for given <paramref name="container"/> to the name specified by <paramref name="targetNamespace"/>.
/// Everything declared in the <paramref name="container"/> will be moved to the new namespace.
/// Change will only be made if <see cref="CanChangeNamespaceAsync"/> returns <see langword="true"/> and <paramref name="targetNamespace"/>
/// is a valid name for namespace.
/// is a valid name for namespace
(we use "" to specify global namespace)
.
/// </summary>
Task
<
Solution
>
ChangeNamespaceAsync
(
Document
document
,
SyntaxNode
container
,
string
targetNamespace
,
CancellationToken
cancellationToken
);
}
...
...
src/Features/VisualBasic/Portable/CodeRefactorings/SyncNamespace/VisualBasicChangeNamespaceService.vb
浏览文件 @
0f9a0009
...
...
@@ -49,7 +49,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeNamespace
End
Function
' This is only reachable when called from a VB service, which is not implemented yet.
Protected
Overrides
Function
GetMemberDeclarationsInContainer
(
co
mpilationUnitOrNamespaceDecl
As
SyntaxNode
)
As
SyntaxList
(
Of
StatementSyntax
)
Protected
Overrides
Function
GetMemberDeclarationsInContainer
(
co
ntainer
As
SyntaxNode
)
As
SyntaxList
(
Of
StatementSyntax
)
Throw
ExceptionUtilities
.
Unreachable
End
Function
...
...
@@ -59,11 +59,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeNamespace
End
Function
' This is only reachable when called from a VB service, which is not implemented yet.
Protected
Overrides
Function
GetDeclaredNamespace
(
node
As
SyntaxNode
)
As
String
Protected
Overrides
Function
GetDeclaredNamespace
(
container
As
SyntaxNode
)
As
String
Throw
ExceptionUtilities
.
Unreachable
End
Function
Private
Function
CreateNameSyntax
(
namespaceParts
As
ImmutableArray
(
Of
String
),
index
As
Integer
)
As
NameSyntax
Private
Shared
Function
CreateNameSyntax
(
namespaceParts
As
ImmutableArray
(
Of
String
),
index
As
Integer
)
As
NameSyntax
Dim
part
=
namespaceParts
(
index
).
EscapeIdentifier
()
Dim
namePiece
=
SyntaxFactory
.
IdentifierName
(
part
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录