Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
f2f61ce8
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,发现更多精彩内容 >>
提交
f2f61ce8
编写于
7月 29, 2019
作者:
P
Petr Houska
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
PR feedback + cleanup and reorg.
上级
9380d197
变更
15
隐藏空白更改
内联
并排
Showing
15 changed file
with
197 addition
and
174 deletion
+197
-174
src/EditorFeatures/CSharpTest/CodeRefactorings/UseExplicitOrImplicitType/UseImplicitTypeRefactoringTests.cs
...ExplicitOrImplicitType/UseImplicitTypeRefactoringTests.cs
+0
-1
src/EditorFeatures/CSharpTest/RefactoringHelpers/RefactoringHelpersTests.cs
.../CSharpTest/RefactoringHelpers/RefactoringHelpersTests.cs
+0
-2
src/EditorFeatures/TestUtilities/RefactoringHelpers/RefactoringHelpersTestBase.cs
...tilities/RefactoringHelpers/RefactoringHelpersTestBase.cs
+2
-2
src/Features/CSharp/Portable/CodeRefactorings/CSharpRefactoringHelpersService.cs
...table/CodeRefactorings/CSharpRefactoringHelpersService.cs
+1
-1
src/Features/CSharp/Portable/NameTupleElement/CSharpNameTupleElementCodeRefactoringProvider.cs
...eElement/CSharpNameTupleElementCodeRefactoringProvider.cs
+1
-1
src/Features/CSharp/Portable/UseNamedArguments/CSharpUseNamedArgumentsCodeRefactoringProvider.cs
...guments/CSharpUseNamedArgumentsCodeRefactoringProvider.cs
+1
-1
src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs
...ble/CodeRefactorings/AbstractRefactoringHelpersService.cs
+161
-132
src/Features/Core/Portable/CodeRefactorings/CodeRefactoringContextExtensions.cs
...able/CodeRefactorings/CodeRefactoringContextExtensions.cs
+15
-17
src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs
...ractConvertAnonymousTypeToClassCodeRefactoringProvider.cs
+2
-2
src/Features/Core/Portable/IntroduceUsingStatement/AbstractIntroduceUsingStatementCodeRefactoringProvider.cs
...AbstractIntroduceUsingStatementCodeRefactoringProvider.cs
+1
-3
src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs
...lement/AbstractNameTupleElementCodeRefactoringProvider.cs
+6
-7
src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs
...ments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs
+4
-2
src/Features/VisualBasic/Portable/CodeRefactorings/VisualBasicRefactoringHelpersService.vb
.../CodeRefactorings/VisualBasicRefactoringHelpersService.vb
+1
-1
src/Features/VisualBasic/Portable/NameTupleElement/VisualBasicNameTupleElementCodeRefactoringProvider.vb
...ent/VisualBasicNameTupleElementCodeRefactoringProvider.vb
+1
-1
src/Features/VisualBasic/Portable/UseNamedArguments/VisualBasicUseNamedArgumentsCodeRefactoringProvider.vb
...ts/VisualBasicUseNamedArgumentsCodeRefactoringProvider.vb
+1
-1
未找到文件。
src/EditorFeatures/CSharpTest/CodeRefactorings/UseExplicitOrImplicitType/UseImplicitTypeRefactoringTests.cs
浏览文件 @
f2f61ce8
...
...
@@ -105,7 +105,6 @@ static void Main()
await
TestMissingInRegularAndScriptAsync
(
code
);
}
[
Fact
]
public
async
Task
TestForeachInsideLocalDeclaration
()
{
...
...
src/EditorFeatures/CSharpTest/RefactoringHelpers/RefactoringHelpersTests.cs
浏览文件 @
f2f61ce8
// 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.Threading.Tasks
;
using
ICSharpCode.Decompiler.CSharp.Syntax
;
using
Microsoft.CodeAnalysis.CSharp.Syntax
;
using
Microsoft.CodeAnalysis.Editor.UnitTests.RefactoringHelpers
;
using
Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
;
using
Roslyn.Test.Utilities
;
using
Roslyn.Utilities
;
using
Xunit
;
namespace
Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RefactoringHelpers
...
...
src/EditorFeatures/TestUtilities/RefactoringHelpers/RefactoringHelpersTestBase.cs
浏览文件 @
f2f61ce8
...
...
@@ -89,9 +89,9 @@ private static string GetSelectionAndResultSpans(string text, out TextSpan selec
private
async
Task
<
TNode
>
GetNodeForSelection
<
TNode
>(
string
text
,
TextSpan
selection
,
Func
<
TNode
,
bool
>
predicate
)
where
TNode
:
SyntaxNode
{
var
document
=
fixture
.
UpdateDocument
(
text
,
SourceCodeKind
.
Regular
);
var
re
sultNode
=
await
document
.
TryGetSelectedNodeAsync
<
TNode
>(
selection
,
predicate
,
CancellationToken
.
None
).
ConfigureAwait
(
false
);
var
re
levantNodes
=
await
document
.
TryGetRelevantNodesAsync
<
TNode
>(
selection
,
CancellationToken
.
None
).
ConfigureAwait
(
false
);
return
re
sultNode
;
return
re
levantNodes
.
FirstOrDefault
(
predicate
)
;
}
}
}
src/Features/CSharp/Portable/CodeRefactorings/CSharpRefactoringHelpersService.cs
浏览文件 @
f2f61ce8
...
...
@@ -11,7 +11,7 @@
namespace
Microsoft.CodeAnalysis.CSharp.CodeRefactorings
{
[
ExportLanguageService
(
typeof
(
IRefactoringHelpersService
),
LanguageNames
.
CSharp
),
Shared
]
internal
class
CSharpRefactoringHelpersService
:
AbstractRefactoringHelpersService
<
ExpressionSyntax
,
ArgumentSyntax
>
internal
class
CSharpRefactoringHelpersService
:
AbstractRefactoringHelpersService
<
ExpressionSyntax
>
{
protected
override
IEnumerable
<
SyntaxNode
>
ExtractNodesSimple
(
SyntaxNode
node
,
ISyntaxFactsService
syntaxFacts
)
{
...
...
src/Features/CSharp/Portable/NameTupleElement/CSharpNameTupleElementCodeRefactoringProvider.cs
浏览文件 @
f2f61ce8
...
...
@@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.NameTupleElement
{
[
ExtensionOrder
(
After
=
PredefinedCodeRefactoringProviderNames
.
IntroduceVariable
)]
[
ExportCodeRefactoringProvider
(
LanguageNames
.
CSharp
,
Name
=
nameof
(
CSharpNameTupleElementCodeRefactoringProvider
)),
Shared
]
internal
class
CSharpNameTupleElementCodeRefactoringProvider
:
AbstractNameTupleElementCodeRefactoringProvider
<
ArgumentSyntax
,
TupleExpressionSyntax
>
internal
class
CSharpNameTupleElementCodeRefactoringProvider
:
AbstractNameTupleElementCodeRefactoringProvider
<
ArgumentSyntax
,
TupleExpressionSyntax
,
ExpressionSyntax
>
{
[
ImportingConstructor
]
public
CSharpNameTupleElementCodeRefactoringProvider
()
...
...
src/Features/CSharp/Portable/UseNamedArguments/CSharpUseNamedArgumentsCodeRefactoringProvider.cs
浏览文件 @
f2f61ce8
...
...
@@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseNamedArguments
[
ExportCodeRefactoringProvider
(
LanguageNames
.
CSharp
,
Name
=
nameof
(
CSharpUseNamedArgumentsCodeRefactoringProvider
)),
Shared
]
internal
class
CSharpUseNamedArgumentsCodeRefactoringProvider
:
AbstractUseNamedArgumentsCodeRefactoringProvider
{
private
abstract
class
BaseAnalyzer
<
TSyntax
,
TSyntaxList
>
:
Analyzer
<
TSyntax
,
TSyntax
,
TSyntaxList
>
private
abstract
class
BaseAnalyzer
<
TSyntax
,
TSyntaxList
>
:
Analyzer
<
TSyntax
,
TSyntax
,
TSyntaxList
,
ExpressionSyntax
>
where
TSyntax
:
SyntaxNode
where
TSyntaxList
:
SyntaxNode
{
...
...
src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs
浏览文件 @
f2f61ce8
...
...
@@ -4,26 +4,23 @@
using
System.Collections.Generic
;
using
System.Collections.Immutable
;
using
System.Linq
;
using
System.Runtime.CompilerServices
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.LanguageServices
;
using
Microsoft.CodeAnalysis.PooledObjects
;
using
Microsoft.CodeAnalysis.Shared.Extensions
;
using
Microsoft.CodeAnalysis.Text
;
using
Roslyn.Utilities
;
namespace
Microsoft.CodeAnalysis.CodeRefactorings
{
internal
abstract
class
AbstractRefactoringHelpersService
<
TExpressionSyntax
,
TArgumentSyntax
>
:
IRefactoringHelpersService
internal
abstract
class
AbstractRefactoringHelpersService
<
TExpressionSyntax
>
:
IRefactoringHelpersService
where
TExpressionSyntax
:
SyntaxNode
where
TArgumentSyntax
:
SyntaxNode
{
public
async
Task
<
ImmutableArray
<
TSyntaxNode
>>
GetRelevantNodesAsync
<
TSyntaxNode
>(
Document
document
,
TextSpan
selectionRaw
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
{
var
relevantNodesBuilder
=
ArrayBuilder
<
TSyntaxNode
>.
GetInstance
();
// Given selection is trimmed first to enable over-selection that spans multiple lines. Since trailing whitespace ends
// at newline boundary over-selection to e.g. a line after LocalFunctionStatement would cause FindNode to find enclosing
// block's Node. That is because in addition to LocalFunctionStatement the selection would also contain trailing trivia
...
...
@@ -43,82 +40,91 @@ internal abstract class AbstractRefactoringHelpersService<TExpressionSyntax, TAr
return
ImmutableArray
<
TSyntaxNode
>.
Empty
;
}
// Every time a Node is considered an extractNodes method is called to add all nodes around the original one
// that should also be considered.
//
// That enables us to e.g. return node `b` when Node `var a = b;` is being considered without a complex (and potentially
// lang. & situation dependent) into Children descending code here. We can't just try extracted Node because we might
// want the whole node `var a = b;`
//
// In addition to per-node extractions we also check if current location (if selection is empty) is in a header of higher level
// desired node once. We do that only for locations because otherwise `[|int|] A { get; set; }) would trigger all refactorings for
// Property Decl.
// Handle selections:
// - The smallest node whose FullSpan includes the whole (trimmed) selection
// - Using FullSpan is important because it handles over-selection with comments
// - Travels upwards through same-sized (FullSpan) nodes, extracting
// - Handles situations where:
// - Token with wanted Node as direct parent is selected (e.g. IdentifierToken for LocalFunctionStatement: `C [|Fun|]() {}`)
// - Most/the whole wanted Node is selected (e.g. `C [|Fun() {}|]`
// Note: Whether we have selection or location has to be checked against original selection because selecting just
// whitespace could collapse selectionTrimmed into and empty Location. But we don't want `[| |]token`
// registering as ` [||]token`.
if
(!
selectionTrimmed
.
IsEmpty
)
var
relevantNodesBuilder
=
ArrayBuilder
<
TSyntaxNode
>.
GetInstance
();
try
{
var
selectionNode
=
root
.
FindNode
(
selectionTrimmed
,
getInnermostNodeForTie
:
true
);
var
prevNode
=
selectionNode
;
do
// Every time a Node is considered an extractNodes method is called to add all nodes around the original one
// that should also be considered.
//
// That enables us to e.g. return node `b` when Node `var a = b;` is being considered without a complex (and potentially
// lang. & situation dependent) into Children descending code here. We can't just try extracted Node because we might
// want the whole node `var a = b;`
// Handle selections:
// - The smallest node whose FullSpan includes the whole (trimmed) selection
// - Using FullSpan is important because it handles over-selection with comments
// - Travels upwards through same-sized (FullSpan) nodes, extracting
// - Handles situations where:
// - Token with wanted Node as direct parent is selected (e.g. IdentifierToken for LocalFunctionStatement: `C [|Fun|]() {}`)
// - Most/the whole wanted Node is selected (e.g. `C [|Fun() {}|]`
// Note: Whether we have selection or location has to be checked against original selection because selecting just
// whitespace could collapse selectionTrimmed into and empty Location. But we don't want `[| |]token`
// registering as ` [||]token`.
if
(!
selectionTrimmed
.
IsEmpty
)
{
var
nonHiddenExtractedSelectedNodes
=
ExtractNodesSimple
(
selectionNode
,
syntaxFacts
).
OfType
<
TSyntaxNode
>().
Where
(
n
=>
!
n
.
OverlapsHiddenPosition
(
cancellationToken
));
foreach
(
var
selectedNode
in
nonHiddenExtractedSelectedNodes
)
AddRelevantNodesForSelection
(
syntaxFacts
,
root
,
selectionTrimmed
,
relevantNodesBuilder
,
cancellationToken
);
}
else
{
// No more selection -> Handle what current selection is touching:
//
// Consider touching only for empty selections. Otherwise `[|C|] methodName(){}` would be considered as
// touching the Method's Node (through the left edge, see below) which is something the user probably
// didn't want since they specifically selected only the return type.
//
// What the selection is touching is used in two ways.
// - Firstly, it is used to handle situation where it touches a Token whose direct ancestor is wanted Node.
// While having the (even empty) selection inside such token or to left of such Token is already handle
// by code above touching it from right `C methodName[||](){}` isn't (the FindNode for that returns Args node).
// - Secondly, it is used for left/right edge climbing. E.g. `[||]C methodName(){}` the touching token's direct
// ancestor is TypeNode for the return type but it is still reasonable to expect that the user might want to
// be given refactorings for the whole method (as he has caret on the edge of it). Therefore we travel the
// Node tree upwards and as long as we're on the left edge of a Node's span we consider such node & potentially
// continue traveling upwards. The situation for right edge (`C methodName(){}[||]`) is analogical.
// E.g. for right edge `C methodName(){}[||]`: CloseBraceToken -> BlockSyntax -> LocalFunctionStatement -> null (higher
// node doesn't end on position anymore)
// Note: left-edge climbing needs to handle AttributeLists explicitly, see below for more information.
// - Thirdly, if location isn't touching anything, we move the location to the token in whose trivia location is in.
// more about that below.
// - Fourthly, if we're in an expression / argument we consider touching a parent expression whenever we're within it
// as long as it is on the first line of such expression (arbitrary heuristic).
// First we need to get tokens we might potentially be touching, tokenToRightOrIn and tokenToLeft.
var
(
tokenToRightOrIn
,
tokenToLeft
,
location
)
=
await
GetTokensToRightOrInToLeftAndUpdatedLocation
(
document
,
root
,
selectionTrimmed
,
cancellationToken
).
ConfigureAwait
(
false
);
// In addition to per-node extr also check if current location (if selection is empty) is in a header of higher level
// desired node once. We do that only for locations because otherwise `[|int|] A { get; set; }) would trigger all refactorings for
// Property Decl.
// We cannot check this any sooner because the above code could've changed current location.
AddNonHiddenCorrectTypeNodes
(
ExtractNodesInHeader
(
root
,
location
,
syntaxFacts
),
relevantNodesBuilder
,
cancellationToken
);
// Add Nodes for touching tokens as described above.
AddNodesForTokenToRightOrIn
(
syntaxFacts
,
root
,
relevantNodesBuilder
,
location
,
tokenToRightOrIn
,
cancellationToken
);
AddNodesForTokenToLeft
(
syntaxFacts
,
relevantNodesBuilder
,
location
,
tokenToLeft
,
cancellationToken
);
// If the wanted node is an expression syntax -> traverse upwards even if location is deep within a SyntaxNode.
if
(
typeof
(
TSyntaxNode
).
IsSubclassOf
(
typeof
(
TExpressionSyntax
))
||
typeof
(
TSyntaxNode
)
==
typeof
(
TExpressionSyntax
))
{
// For selections we need to handle an edge case where only AttributeLists are within selection (e.g. `Func([|[in][out]|] arg1);`).
// In that case the smallest encompassing node is still the whole argument node but it's hard to justify showing refactorings for it
// if user selected only its attributes.
// Selection contains only AttributeLists -> don't consider current Node
var
spanWithoutAttributes
=
GetSpanWithoutAttributes
(
selectedNode
,
root
,
syntaxFacts
);
if
(!
selectionTrimmed
.
IntersectsWith
(
spanWithoutAttributes
))
{
break
;
}
relevantNodesBuilder
.
Add
(
selectedNode
);
await
AddNodesDeepInExpression
(
document
,
location
,
relevantNodesBuilder
,
cancellationToken
).
ConfigureAwait
(
false
);
}
prevNode
=
selectionNode
;
selectionNode
=
selectionNode
.
Parent
;
}
while
(
selectionNode
!=
null
&&
prevNode
.
FullWidth
()
==
selectionNode
.
FullWidth
());
return
relevantNodesBuilder
.
ToImmutable
AndFree
();
return
relevantNodesBuilder
.
ToImmutable
();
}
finally
{
relevantNodesBuilder
.
Free
();
}
}
// Handle what current selection is touching:
//
// Consider touching only for empty selections. Otherwise `[|C|] methodName(){}` would be considered as
// touching the Method's Node (through the left edge, see below) which is something the user probably
// didn't want since they specifically selected only the return type.
//
// What the selection is touching is used in two ways.
// - Firstly, it is used to handle situation where it touches a Token whose direct ancestor is wanted Node.
// While having the (even empty) selection inside such token or to left of such Token is already handle
// by code above touching it from right `C methodName[||](){}` isn't (the FindNode for that returns Args node).
// - Secondly, it is used for left/right edge climbing. E.g. `[||]C methodName(){}` the touching token's direct
// ancestor is TypeNode for the return type but it is still reasonable to expect that the user might want to
// be given refactorings for the whole method (as he has caret on the edge of it). Therefore we travel the
// Node tree upwards and as long as we're on the left edge of a Node's span we consider such node & potentially
// continue traveling upwards. The situation for right edge (`C methodName(){}[||]`) is analogical.
// E.g. for right edge `C methodName(){}[||]`: CloseBraceToken -> BlockSyntax -> LocalFunctionStatement -> null (higher
// node doesn't end on position anymore)
// Note: left-edge climbing needs to handle AttributeLists explicitly, see below for more information.
// - Thirdly, if location isn't touching anything, we move the location to the token in whose trivia location is in.
// more about that below.
// - Fourthly, if we're in an expression / argument we consider touching a parent expression whenever we're within it
// as long as it is on the first line of such expression (arbitrary heuristic).
private
async
Task
<(
SyntaxToken
tokenToRightOrIn
,
SyntaxToken
tokenToLeft
,
int
location
)>
GetTokensToRightOrInToLeftAndUpdatedLocation
(
Document
document
,
SyntaxNode
root
,
TextSpan
selectionTrimmed
,
CancellationToken
cancellationToken
)
{
// get Token for current location
var
location
=
selectionTrimmed
.
Start
;
var
tokenOnLocation
=
root
.
FindToken
(
location
);
...
...
@@ -175,14 +181,41 @@ internal abstract class AbstractRefactoringHelpersService<TExpressionSyntax, TAr
}
}
// We add all nodes we're in a header of.
// We can't check any sooner because the code above (that figures out tokenToLeft, ...) can change current
// `location`.
AddNonHiddenCorrectTypeNodes
(
ExtractNodesInHeader
(
root
,
location
,
syntaxFacts
),
relevantNodesBuilder
,
cancellationToken
);
return
(
tokenToRightOrIn
,
tokenToLeft
,
location
);
}
private
void
AddNodesForTokenToLeft
<
TSyntaxNode
>(
ISyntaxFactsService
syntaxFacts
,
ArrayBuilder
<
TSyntaxNode
>
relevantNodesBuilder
,
int
location
,
SyntaxToken
tokenToLeft
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
{
// there could be multiple (n) tokens to the left if first n-1 are Empty -> iterate over all of them
while
(
tokenToLeft
!=
default
)
{
var
leftNode
=
tokenToLeft
.
Parent
;
do
{
// Consider either a Node that is:
// - Ancestor Node of such Token as long as their span ends on location (it's still on the edge)
AddNonHiddenCorrectTypeNodes
(
ExtractNodesSimple
(
leftNode
,
syntaxFacts
),
relevantNodesBuilder
,
cancellationToken
);
leftNode
=
leftNode
.
Parent
;
if
(
leftNode
==
null
||
!(
leftNode
.
GetLastToken
().
Span
.
End
==
location
||
leftNode
.
Span
.
End
==
location
))
{
break
;
}
}
while
(
true
);
// as long as current tokenToLeft is empty -> its previous token is also tokenToLeft
tokenToLeft
=
tokenToLeft
.
Span
.
IsEmpty
?
tokenToLeft
.
GetPreviousToken
(
includeZeroWidth
:
true
)
:
default
;
}
}
private
void
AddNodesForTokenToRightOrIn
<
TSyntaxNode
>(
ISyntaxFactsService
syntaxFacts
,
SyntaxNode
root
,
ArrayBuilder
<
TSyntaxNode
>
relevantNodesBuilder
,
int
location
,
SyntaxToken
tokenToRightOrIn
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
{
if
(
tokenToRightOrIn
!=
default
)
{
var
rightNode
=
token
OnLocatio
n
.
Parent
;
var
rightNode
=
token
ToRightOrI
n
.
Parent
;
do
{
// Consider either a Node that is:
...
...
@@ -214,71 +247,35 @@ internal abstract class AbstractRefactoringHelpersService<TExpressionSyntax, TAr
}
while
(
true
);
}
}
// there could be multiple (n) tokens to the left if first n-1 are Empty -> iterate over all of them
while
(
tokenToLeft
!=
default
)
private
void
AddRelevantNodesForSelection
<
TSyntaxNode
>(
ISyntaxFactsService
syntaxFacts
,
SyntaxNode
root
,
TextSpan
selectionTrimmed
,
ArrayBuilder
<
TSyntaxNode
>
relevantNodesBuilder
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
{
var
selectionNode
=
root
.
FindNode
(
selectionTrimmed
,
getInnermostNodeForTie
:
true
);
var
prevNode
=
selectionNode
;
do
{
var
leftNode
=
tokenToLeft
.
Parent
;
do
var
nonHiddenExtractedSelectedNodes
=
ExtractNodesSimple
(
selectionNode
,
syntaxFacts
).
OfType
<
TSyntaxNode
>().
Where
(
n
=>
!
n
.
OverlapsHiddenPosition
(
cancellationToken
))
;
foreach
(
var
selectedNode
in
nonHiddenExtractedSelectedNodes
)
{
//
Consider either a Node that is:
//
- Ancestor Node of such Token as long as their span ends on location (it's still on the edge)
AddNonHiddenCorrectTypeNodes
(
ExtractNodesSimple
(
leftNode
,
syntaxFacts
),
relevantNodesBuilder
,
cancellationToken
);
//
For selections we need to handle an edge case where only AttributeLists are within selection (e.g. `Func([|[in][out]|] arg1);`).
//
In that case the smallest encompassing node is still the whole argument node but it's hard to justify showing refactorings for it
// if user selected only its attributes.
leftNode
=
leftNode
.
Parent
;
if
(
leftNode
==
null
||
!(
leftNode
.
GetLastToken
().
Span
.
End
==
location
||
leftNode
.
Span
.
End
==
location
))
// Selection contains only AttributeLists -> don't consider current Node
var
spanWithoutAttributes
=
GetSpanWithoutAttributes
(
selectedNode
,
root
,
syntaxFacts
);
if
(!
selectionTrimmed
.
IntersectsWith
(
spanWithoutAttributes
))
{
break
;
}
}
while
(
true
);
// as long as current tokenToLeft is empty -> its previous token is also tokenToLeft
tokenToLeft
=
tokenToLeft
.
Span
.
IsEmpty
?
tokenToLeft
.
GetPreviousToken
(
includeZeroWidth
:
true
)
:
default
;
}
// If the wanted node is an expression syntax -> traverse upwards even if location is deep within a SyntaxNode
// ArgumentSyntax isn't an "expression" syntax but we want to treat it as such
if
(
typeof
(
TSyntaxNode
).
IsSubclassOf
(
typeof
(
TExpressionSyntax
))
||
typeof
(
TSyntaxNode
)
==
typeof
(
TArgumentSyntax
))
{
await
AddNodesDeepInExpression
(
document
,
location
,
relevantNodesBuilder
,
cancellationToken
).
ConfigureAwait
(
false
);
}
return
relevantNodesBuilder
.
ToImmutableAndFree
();
static
void
AddNonHiddenCorrectTypeNodes
(
IEnumerable
<
SyntaxNode
>
nodes
,
ArrayBuilder
<
TSyntaxNode
>
resultBuilder
,
CancellationToken
cancellationToken
)
{
var
correctTypeNonHiddenNodes
=
nodes
.
OfType
<
TSyntaxNode
>().
Where
(
n
=>
!
n
.
OverlapsHiddenPosition
(
cancellationToken
));
foreach
(
var
nodeToBeAdded
in
correctTypeNonHiddenNodes
)
{
resultBuilder
.
Add
(
nodeToBeAdded
);
relevantNodesBuilder
.
Add
(
selectedNode
);
}
}
}
private
static
TextSpan
GetSpanWithoutAttributes
(
SyntaxNode
node
,
SyntaxNode
root
,
ISyntaxFactsService
syntaxFacts
)
{
// Span without AttributeLists
// - No AttributeLists -> original .Span
// - Some AttributeLists -> (first non-trivia/comment Token.Span.Begin, original.Span.End)
// - We need to be mindful about comments due to:
// // [Test1]
// //Comment1
// [||]object Property1 { get; set; }
// the comment node being part of the next token's (`object`) leading trivia and not the AttributeList's node.
var
attributeList
=
syntaxFacts
.
GetAttributeLists
(
node
);
if
(
attributeList
.
Any
())
{
var
endOfAttributeLists
=
attributeList
.
Last
().
Span
.
End
;
var
afterAttributesToken
=
root
.
FindTokenOnRightOfPosition
(
endOfAttributeLists
);
return
TextSpan
.
FromBounds
(
afterAttributesToken
.
Span
.
Start
,
node
.
Span
.
End
);
prevNode
=
selectionNode
;
selectionNode
=
selectionNode
.
Parent
;
}
return
node
.
Span
;
while
(
selectionNode
!=
null
&&
prevNode
.
FullWidth
()
==
selectionNode
.
FullWidth
());
}
/// <summary>
...
...
@@ -441,5 +438,37 @@ protected virtual IEnumerable<SyntaxNode> ExtractNodesInHeader(SyntaxNode root,
ancestor
=
ancestor
.
Parent
;
}
}
private
static
TextSpan
GetSpanWithoutAttributes
(
SyntaxNode
node
,
SyntaxNode
root
,
ISyntaxFactsService
syntaxFacts
)
{
// Span without AttributeLists
// - No AttributeLists -> original .Span
// - Some AttributeLists -> (first non-trivia/comment Token.Span.Begin, original.Span.End)
// - We need to be mindful about comments due to:
// // [Test1]
// //Comment1
// [||]object Property1 { get; set; }
// the comment node being part of the next token's (`object`) leading trivia and not the AttributeList's node.
var
attributeList
=
syntaxFacts
.
GetAttributeLists
(
node
);
if
(
attributeList
.
Any
())
{
var
endOfAttributeLists
=
attributeList
.
Last
().
Span
.
End
;
var
afterAttributesToken
=
root
.
FindTokenOnRightOfPosition
(
endOfAttributeLists
);
return
TextSpan
.
FromBounds
(
afterAttributesToken
.
Span
.
Start
,
node
.
Span
.
End
);
}
return
node
.
Span
;
}
void
AddNonHiddenCorrectTypeNodes
<
TSyntaxNode
>(
IEnumerable
<
SyntaxNode
>
nodes
,
ArrayBuilder
<
TSyntaxNode
>
resultBuilder
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
{
var
correctTypeNonHiddenNodes
=
nodes
.
OfType
<
TSyntaxNode
>().
Where
(
n
=>
!
n
.
OverlapsHiddenPosition
(
cancellationToken
));
foreach
(
var
nodeToBeAdded
in
correctTypeNonHiddenNodes
)
{
resultBuilder
.
Add
(
nodeToBeAdded
);
}
}
}
}
src/Features/Core/Portable/CodeRefactorings/CodeRefactoringContextExtensions.cs
浏览文件 @
f2f61ce8
...
...
@@ -8,7 +8,6 @@
using
Microsoft.CodeAnalysis.CodeActions
;
using
Microsoft.CodeAnalysis.Shared.Extensions
;
using
Microsoft.CodeAnalysis.Text
;
using
Roslyn.Utilities
;
namespace
Microsoft.CodeAnalysis.CodeRefactorings
{
...
...
@@ -30,29 +29,28 @@ internal static class CodeRefactoringContextExtensions
}
}
internal
static
async
Task
<
TSyntaxNode
>
TryGetSelectedNodeAsync
<
TSyntaxNode
>(
this
CodeRefactoringContext
context
)
internal
static
Task
<
TSyntaxNode
>
TryGetSelectedNodeAsync
<
TSyntaxNode
>(
this
CodeRefactoringContext
context
)
where
TSyntaxNode
:
SyntaxNode
{
(
var
document
,
var
span
,
var
cancellationToken
)
=
context
;
var
potentialNodes
=
await
GetRelevantNodes
<
TSyntaxNode
>(
document
,
span
,
cancellationToken
).
ConfigureAwait
(
false
);
return
potentialNodes
.
FirstOrDefault
();
}
=>
TryGetRelevantNodeAsync
<
TSyntaxNode
>(
context
.
Document
,
context
.
Span
,
context
.
CancellationToken
);
internal
static
async
Task
<
TSyntaxNode
>
TryGetSelectedNodeAsync
<
TSyntaxNode
>(
this
Document
document
,
TextSpan
span
,
Func
<
TSyntaxNode
,
bool
>
predicate
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
{
var
potentialNodes
=
await
GetRelevantNodes
<
TSyntaxNode
>(
document
,
span
,
cancellationToken
).
ConfigureAwait
(
false
);
return
potentialNodes
.
FirstOrDefault
(
predicate
);
}
internal
static
Task
<
ImmutableArray
<
TSyntaxNode
>>
TryGetSelectedNodesAsync
<
TSyntaxNode
>(
this
CodeRefactoringContext
context
)
where
TSyntaxNode
:
SyntaxNode
=>
TryGetRelevantNodesAsync
<
TSyntaxNode
>(
context
.
Document
,
context
.
Span
,
context
.
CancellationToken
);
internal
static
async
Task
<
TSyntaxNode
>
TryGetSelectedNodeAsync
<
TSyntaxNode
>(
this
Document
document
,
TextSpan
span
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
internal
static
async
Task
<
TSyntaxNode
>
TryGetRelevantNodeAsync
<
TSyntaxNode
>(
this
Document
document
,
TextSpan
span
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
{
var
potentialNodes
=
await
GetRelevantNodes
<
TSyntaxNode
>(
document
,
span
,
cancellationToken
).
ConfigureAwait
(
false
);
var
potentialNodes
=
await
TryGetRelevantNodesAsync
<
TSyntaxNode
>(
document
,
span
,
cancellationToken
).
ConfigureAwait
(
false
);
return
potentialNodes
.
FirstOrDefault
();
}
private
static
async
Task
<
ImmutableArray
<
TSyntaxNode
>>
GetRelevantNodes
<
TSyntaxNode
>(
Document
document
,
TextSpan
span
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
internal
static
async
Task
<
ImmutableArray
<
TSyntaxNode
>>
TryGetRelevantNodesAsync
<
TSyntaxNode
>(
this
Document
document
,
TextSpan
span
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
{
var
helpers
=
document
.
GetLanguageService
<
IRefactoringHelpersService
>();
var
potentialNodes
=
await
helpers
.
GetRelevantNodesAsync
<
TSyntaxNode
>(
document
,
span
,
cancellationToken
).
ConfigureAwait
(
false
);
...
...
src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs
浏览文件 @
f2f61ce8
...
...
@@ -71,8 +71,8 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
// Due to the way `TryGetSelectedNodeAsync` works and how `TAnonymousObjectCreationExpressionSyntax` is e.g. for C# constructed
// it matches even when caret is next to some tokens within the anonymous object creation node.
// E.g.: `var a = new [||]{ b=1,[||] c=2 };` both match due to the caret being next to `,` and `{`.
var
helper
=
document
.
GetLanguageService
<
IRefactoringHelpersService
>();
var
anonymousObject
=
await
document
.
TryGetSelectedNodeAsync
<
TAnonymousObjectCreationExpressionSyntax
>(
span
,
cancellationToken
).
ConfigureAwait
(
false
);
var
anonymousObject
=
await
document
.
TryGetRelevantNodeAsync
<
TAnonymousObjectCreationExpressionSyntax
>(
span
,
cancellationToken
).
ConfigureAwait
(
false
);
if
(
anonymousObject
==
null
)
{
return
default
;
...
...
src/Features/Core/Portable/IntroduceUsingStatement/AbstractIntroduceUsingStatementCodeRefactoringProvider.cs
浏览文件 @
f2f61ce8
...
...
@@ -46,9 +46,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
private
async
Task
<
TLocalDeclarationSyntax
>
FindDisposableLocalDeclaration
(
Document
document
,
TextSpan
selection
,
CancellationToken
cancellationToken
)
{
var
refactoringHelperService
=
document
.
GetLanguageService
<
IRefactoringHelpersService
>();
var
declarationSyntax
=
await
document
.
TryGetSelectedNodeAsync
<
TLocalDeclarationSyntax
>(
selection
,
cancellationToken
).
ConfigureAwait
(
false
);
var
declarationSyntax
=
await
document
.
TryGetRelevantNodeAsync
<
TLocalDeclarationSyntax
>(
selection
,
cancellationToken
).
ConfigureAwait
(
false
);
if
(
declarationSyntax
is
null
||
!
CanRefactorToContainBlockStatements
(
declarationSyntax
.
Parent
))
{
return
default
;
...
...
src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs
浏览文件 @
f2f61ce8
...
...
@@ -9,13 +9,13 @@
using
Microsoft.CodeAnalysis.LanguageServices
;
using
Microsoft.CodeAnalysis.Shared.Extensions
;
using
Microsoft.CodeAnalysis.Text
;
using
Roslyn.Utilities
;
namespace
Microsoft.CodeAnalysis.NameTupleElement
{
abstract
class
AbstractNameTupleElementCodeRefactoringProvider
<
TArgumentSyntax
,
TTupleExpressionSyntax
>
:
CodeRefactoringProvider
abstract
class
AbstractNameTupleElementCodeRefactoringProvider
<
TArgumentSyntax
,
TTupleExpressionSyntax
,
TExpresionSyntax
>
:
CodeRefactoringProvider
where
TArgumentSyntax
:
SyntaxNode
where
TTupleExpressionSyntax
:
SyntaxNode
where
TExpresionSyntax
:
SyntaxNode
where
TTupleExpressionSyntax
:
TExpresionSyntax
{
protected
abstract
bool
IsCloseParenOrComma
(
SyntaxToken
token
);
protected
abstract
TArgumentSyntax
WithName
(
TArgumentSyntax
argument
,
string
argumentName
);
...
...
@@ -44,11 +44,10 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
return
default
;
}
var
helperService
=
document
.
GetLanguageService
<
IRefactoringHelpersService
>();
var
syntaxFacts
=
document
.
GetLanguageService
<
ISyntaxFactsService
>();
Func
<
SyntaxNode
,
bool
>
isTupleArgumentExpression
=
n
=>
n
.
Parent
!=
null
&&
syntaxFacts
.
IsTupleExpression
(
n
.
Parent
);
var
argument
=
await
document
.
TryGetSelectedNodeAsync
<
TArgumentSyntax
>(
span
,
isTupleArgumentExpression
,
cancellationToken
).
ConfigureAwait
(
false
)
;
var
expressions
=
await
document
.
TryGetRelevantNodesAsync
<
TExpresionSyntax
>(
span
,
cancellationToken
).
ConfigureAwait
(
false
);
var
argument
=
expressions
.
FirstOrDefault
(
n
=>
n
.
Parent
is
TArgumentSyntax
&&
n
.
Parent
.
Parent
is
TTupleExpressionSyntax
)?.
Parent
as
TArgumentSyntax
;
if
(
argument
==
null
||
!
syntaxFacts
.
IsSimpleArgument
(
argument
))
{
return
default
;
...
...
src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs
浏览文件 @
f2f61ce8
...
...
@@ -20,17 +20,19 @@ protected interface IAnalyzer
Task
ComputeRefactoringsAsync
(
CodeRefactoringContext
context
,
SyntaxNode
root
);
}
protected
abstract
class
Analyzer
<
TBaseArgumentSyntax
,
TSimpleArgumentSyntax
,
TArgumentListSyntax
>
:
IAnalyzer
protected
abstract
class
Analyzer
<
TBaseArgumentSyntax
,
TSimpleArgumentSyntax
,
TArgumentListSyntax
,
TExpressionSyntax
>
:
IAnalyzer
where
TBaseArgumentSyntax
:
SyntaxNode
where
TSimpleArgumentSyntax
:
TBaseArgumentSyntax
where
TArgumentListSyntax
:
SyntaxNode
where
TExpressionSyntax
:
SyntaxNode
{
public
async
Task
ComputeRefactoringsAsync
(
CodeRefactoringContext
context
,
SyntaxNode
root
)
{
var
(
document
,
textSpan
,
cancellationToken
)
=
context
;
var
argument
=
await
context
.
TryGetSelectedNodeAsync
<
TSimpleArgumentSyntax
>().
ConfigureAwait
(
false
);
var
expressions
=
await
context
.
TryGetSelectedNodesAsync
<
TExpressionSyntax
>().
ConfigureAwait
(
false
);
var
argument
=
expressions
.
FirstOrDefault
(
n
=>
n
is
TExpressionSyntax
)?.
Parent
as
TSimpleArgumentSyntax
;
if
(
argument
==
null
)
{
return
;
...
...
src/Features/VisualBasic/Portable/CodeRefactorings/VisualBasicRefactoringHelpersService.vb
浏览文件 @
f2f61ce8
...
...
@@ -9,7 +9,7 @@ Imports Microsoft.CodeAnalysis.LanguageServices
Namespace
Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings
<
ExportLanguageService
(
GetType
(
IRefactoringHelpersService
),
LanguageNames
.
VisualBasic
),
[
Shared
]
>
Friend
Class
VisualBasicRefactoringHelpersService
Inherits
AbstractRefactoringHelpersService
(
Of
ExpressionSyntax
,
ArgumentSyntax
)
Inherits
AbstractRefactoringHelpersService
(
Of
ExpressionSyntax
)
Protected
Overrides
Iterator
Function
ExtractNodesSimple
(
node
As
SyntaxNode
,
syntaxFacts
As
ISyntaxFactsService
)
As
IEnumerable
(
Of
SyntaxNode
)
For
Each
baseExtraction
In
MyBase
.
ExtractNodesSimple
(
node
,
syntaxFacts
)
...
...
src/Features/VisualBasic/Portable/NameTupleElement/VisualBasicNameTupleElementCodeRefactoringProvider.vb
浏览文件 @
f2f61ce8
...
...
@@ -9,7 +9,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.NameTupleElement
<
ExtensionOrder
(
After
:
=
PredefinedCodeRefactoringProviderNames
.
IntroduceVariable
)
>
<
ExportCodeRefactoringProvider
(
LanguageNames
.
VisualBasic
,
Name
:
=
NameOf
(
VisualBasicNameTupleElementCodeRefactoringProvider
)),
[
Shared
]
>
Friend
Class
VisualBasicNameTupleElementCodeRefactoringProvider
Inherits
AbstractNameTupleElementCodeRefactoringProvider
(
Of
SimpleArgumentSyntax
,
TupleExpressionSyntax
)
Inherits
AbstractNameTupleElementCodeRefactoringProvider
(
Of
SimpleArgumentSyntax
,
TupleExpressionSyntax
,
ExpressionSyntax
)
<
ImportingConstructor
>
Public
Sub
New
()
...
...
src/Features/VisualBasic/Portable/UseNamedArguments/VisualBasicUseNamedArgumentsCodeRefactoringProvider.vb
浏览文件 @
f2f61ce8
...
...
@@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseNamedArguments
Inherits
AbstractUseNamedArgumentsCodeRefactoringProvider
Private
Class
ArgumentAnalyzer
Inherits
Analyzer
(
Of
ArgumentSyntax
,
SimpleArgumentSyntax
,
ArgumentListSyntax
)
Inherits
Analyzer
(
Of
ArgumentSyntax
,
SimpleArgumentSyntax
,
ArgumentListSyntax
,
ExpressionSyntax
)
Protected
Overrides
Function
IsPositionalArgument
(
argument
As
SimpleArgumentSyntax
)
As
Boolean
Return
argument
.
NameColonEquals
Is
Nothing
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录