Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
022bf3ae
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,发现更多精彩内容 >>
提交
022bf3ae
编写于
11月 10, 2019
作者:
C
Cyrus Najmabadi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add feature to easily switch from explicit to implicit impls
上级
ff33ac8f
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
187 addition
and
5 deletion
+187
-5
src/Features/CSharp/Portable/ImplementInterface/CSharpImplementImplicitlyCodeRefactoringProvider.cs
...rface/CSharpImplementImplicitlyCodeRefactoringProvider.cs
+177
-0
src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs
...s/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs
+3
-0
src/Workspaces/Core/Portable/Shared/Extensions/DocumentExtensions.cs
...ces/Core/Portable/Shared/Extensions/DocumentExtensions.cs
+7
-5
未找到文件。
src/Features/CSharp/Portable/ImplementInterface/CSharpImplementImplicitlyCodeRefactoringProvider.cs
0 → 100644
浏览文件 @
022bf3ae
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#
nullable
enable
using
System
;
using
System.Collections.Generic
;
using
System.Composition
;
using
System.Linq
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.CodeActions
;
using
Microsoft.CodeAnalysis.CodeRefactorings
;
using
Microsoft.CodeAnalysis.CSharp.Syntax
;
using
Microsoft.CodeAnalysis.Editing
;
using
Microsoft.CodeAnalysis.PooledObjects
;
using
Microsoft.CodeAnalysis.Shared.Extensions
;
using
Microsoft.CodeAnalysis.Text
;
using
Roslyn.Utilities
;
namespace
Microsoft.CodeAnalysis.CSharp.ImplementInterface
{
[
ExportCodeRefactoringProvider
(
LanguageNames
.
CSharp
),
Shared
]
internal
class
CSharpImplementImplicitlyCodeRefactoringProvider
:
CodeRefactoringProvider
{
public
override
async
Task
ComputeRefactoringsAsync
(
CodeRefactoringContext
context
)
{
var
(
document
,
span
,
cancellationToken
)
=
context
;
var
tree
=
await
document
.
GetRequiredSyntaxTreeAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
root
=
await
document
.
GetRequiredSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
token
=
root
.
FindToken
(
span
.
Start
);
// Move back if the user is at: X.Goo$$(
if
(
span
.
IsEmpty
&&
token
.
Kind
()
==
SyntaxKind
.
OpenParenToken
)
token
=
token
.
GetPreviousToken
();
var
(
container
,
explicitName
,
name
)
=
GetContainer
(
token
);
if
(
explicitName
==
null
)
return
;
var
applicableSpan
=
TextSpan
.
FromBounds
(
explicitName
.
FullSpan
.
Start
,
name
.
FullSpan
.
End
);
if
(!
applicableSpan
.
Contains
(
span
))
return
;
var
semanticModel
=
await
document
.
GetSemanticModelAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
member
=
semanticModel
.
GetDeclaredSymbol
(
container
,
cancellationToken
)
??
throw
new
InvalidOperationException
();
var
explicitImpls
=
member
.
ExplicitInterfaceImplementations
();
if
(
explicitImpls
.
Length
!=
1
)
return
;
var
explicitImpl
=
explicitImpls
[
0
];
var
interfaceType
=
explicitImpl
.
ContainingType
;
var
interfaceName
=
interfaceType
.
ToDisplayString
(
SymbolDisplayFormat
.
MinimallyQualifiedFormat
);
var
solution
=
document
.
Project
.
Solution
;
var
codeAction
=
new
MyCodeAction
(
$"Implicit
{
explicitImpl
.
Name
}
implicitly"
,
c
=>
ImplementImplicitlyAsync
(
solution
,
new
HashSet
<
ISymbol
>
{
member
},
c
));
var
containingType
=
member
.
ContainingType
;
var
explicitlyImplementedMembers
=
GetExplicitlyImplementedMembers
(
containingType
,
interfaceType
).
ToSet
();
// There was only one member in the interface that we implement. Only need to show
// the single action.
if
(
explicitlyImplementedMembers
.
Count
<=
1
)
{
context
.
RegisterRefactoring
(
codeAction
);
return
;
}
var
nestedActions
=
ArrayBuilder
<
CodeAction
>.
GetInstance
();
nestedActions
.
Add
(
codeAction
);
if
(
explicitlyImplementedMembers
.
Count
>
1
)
nestedActions
.
Add
(
new
MyCodeAction
(
$"Implicit
{
interfaceName
}
implicitly"
,
c
=>
ImplementImplicitlyAsync
(
solution
,
explicitlyImplementedMembers
,
c
)));
var
allExplicitlyImplementedMembers
=
containingType
.
AllInterfaces
.
SelectMany
(
i
=>
GetExplicitlyImplementedMembers
(
containingType
,
i
)).
ToSet
();
if
(
allExplicitlyImplementedMembers
.
Count
>
explicitlyImplementedMembers
.
Count
)
nestedActions
.
Add
(
new
MyCodeAction
(
$"Implicit all interfaces implicitly"
,
c
=>
ImplementImplicitlyAsync
(
solution
,
allExplicitlyImplementedMembers
,
c
)));
context
.
RegisterRefactoring
(
new
CodeAction
.
CodeActionWithNestedActions
(
"Implicit implicitly"
,
nestedActions
.
ToImmutableAndFree
(),
isInlinable
:
true
));
}
private
IEnumerable
<
ISymbol
>
GetExplicitlyImplementedMembers
(
INamedTypeSymbol
containingType
,
INamedTypeSymbol
interfaceType
)
=>
from
interfaceMember
in
interfaceType
.
GetMembers
()
let
impl
=
containingType
.
FindImplementationForInterfaceMember
(
interfaceMember
)
where
impl
!=
null
where
containingType
.
Equals
(
impl
.
ContainingType
)
where
impl
.
ExplicitInterfaceImplementations
().
Length
>
0
select
impl
;
private
async
Task
<
Solution
>
ImplementImplicitlyAsync
(
Solution
solution
,
ISet
<
ISymbol
>
implementations
,
CancellationToken
cancellationToken
)
{
// First, bucket all the implemented members by which document they appear in.
// That way, we can update all the members in a specific document in bulk.
var
documentToImplDeclarations
=
new
MultiDictionary
<
Document
,
SyntaxNode
>();
foreach
(
var
impl
in
implementations
)
{
foreach
(
var
syntaxRef
in
impl
.
DeclaringSyntaxReferences
)
{
var
doc
=
solution
.
GetDocument
(
syntaxRef
.
SyntaxTree
);
if
(
doc
!=
null
)
{
var
decl
=
await
syntaxRef
.
GetSyntaxAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
if
(
decl
!=
null
)
documentToImplDeclarations
.
Add
(
doc
,
decl
);
}
}
}
var
currentSolution
=
solution
;
foreach
(
var
(
document
,
decls
)
in
documentToImplDeclarations
)
{
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
editor
=
new
SyntaxEditor
(
root
,
solution
.
Workspace
);
foreach
(
var
decl
in
decls
)
{
var
updatedDecl
=
ImplementImplicitly
(
editor
.
Generator
,
decl
);
if
(
updatedDecl
!=
null
)
editor
.
ReplaceNode
(
decl
,
updatedDecl
);
}
currentSolution
=
currentSolution
.
WithDocumentSyntaxRoot
(
document
.
Id
,
editor
.
GetChangedRoot
());
}
return
currentSolution
;
}
private
SyntaxNode
ImplementImplicitly
(
SyntaxGenerator
generator
,
SyntaxNode
decl
)
=>
generator
.
WithAccessibility
(
WithoutExplicitImpl
(
decl
),
Accessibility
.
Public
);
private
SyntaxNode
?
WithoutExplicitImpl
(
SyntaxNode
decl
)
=>
decl
switch
{
MethodDeclarationSyntax
member
=>
member
.
WithExplicitInterfaceSpecifier
(
null
),
PropertyDeclarationSyntax
member
=>
member
.
WithExplicitInterfaceSpecifier
(
null
),
EventDeclarationSyntax
member
=>
member
.
WithExplicitInterfaceSpecifier
(
null
),
_
=>
null
,
};
private
(
SyntaxNode
,
ExplicitInterfaceSpecifierSyntax
?,
SyntaxToken
)
GetContainer
(
SyntaxToken
token
)
{
for
(
var
node
=
token
.
Parent
;
node
!=
null
;
node
=
node
.
Parent
)
{
switch
(
node
)
{
case
MethodDeclarationSyntax
method
:
return
(
method
,
method
.
ExplicitInterfaceSpecifier
,
method
.
Identifier
);
case
PropertyDeclarationSyntax
property
:
return
(
property
,
property
.
ExplicitInterfaceSpecifier
,
property
.
Identifier
);
case
EventDeclarationSyntax
ev
:
return
(
ev
,
ev
.
ExplicitInterfaceSpecifier
,
ev
.
Identifier
);
}
}
return
default
;
}
private
class
MyCodeAction
:
CodeAction
.
SolutionChangeAction
{
public
MyCodeAction
(
string
title
,
Func
<
CancellationToken
,
Task
<
Solution
>>
createChangedSolution
)
:
base
(
title
,
createChangedSolution
)
{
}
}
}
}
src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs
浏览文件 @
022bf3ae
...
...
@@ -1326,6 +1326,9 @@ private SyntaxNode AsMemberOf(SyntaxNode declaration, SyntaxNode member)
internal
override
bool
CanHaveAccessibility
(
SyntaxNode
declaration
)
{
if
(
declaration
==
null
)
return
false
;
switch
(
declaration
.
Kind
())
{
case
SyntaxKind
.
ClassDeclaration
:
...
...
src/Workspaces/Core/Portable/Shared/Extensions/DocumentExtensions.cs
浏览文件 @
022bf3ae
...
...
@@ -33,14 +33,16 @@ internal static partial class DocumentExtensions
public
static
async
Task
<
SyntaxTree
>
GetRequiredSyntaxTreeAsync
(
this
Document
document
,
CancellationToken
cancellationToken
)
{
var
syntaxTree
=
await
document
.
GetSyntaxTreeAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
if
(
syntaxTree
==
null
)
{
throw
new
InvalidOperationException
(
string
.
Format
(
WorkspacesResources
.
SyntaxTree_is_required_to_accomplish_the_task_but_is_not_supported_by_document_0
,
document
.
Name
));
}
return
syntaxTree
??
throw
new
InvalidOperationException
(
string
.
Format
(
WorkspacesResources
.
SyntaxTree_is_required_to_accomplish_the_task_but_is_not_supported_by_document_0
,
document
.
Name
));
}
return
syntaxTree
;
public
static
async
Task
<
SyntaxNode
>
GetRequiredSyntaxRootAsync
(
this
Document
document
,
CancellationToken
cancellationToken
)
{
var
root
=
await
document
.
GetSyntaxRootAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
return
root
??
throw
new
InvalidOperationException
(
string
.
Format
(
WorkspacesResources
.
SyntaxTree_is_required_to_accomplish_the_task_but_is_not_supported_by_document_0
,
document
.
Name
));
}
public
static
bool
IsOpen
(
this
Document
document
)
{
var
workspace
=
document
.
Project
.
Solution
.
Workspace
as
Workspace
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录