Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
9fbc2767
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,发现更多精彩内容 >>
提交
9fbc2767
编写于
8月 05, 2019
作者:
P
Petr Houska
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Move RefactoringHelper to pooled obj. disposer.
上级
768849b0
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
68 addition
and
74 deletion
+68
-74
src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs
...ble/CodeRefactorings/AbstractRefactoringHelpersService.cs
+68
-74
未找到文件。
src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs
浏览文件 @
9fbc2767
...
...
@@ -40,85 +40,79 @@ internal abstract class AbstractRefactoringHelpersService<TExpressionSyntax, TAr
return
ImmutableArray
<
TSyntaxNode
>.
Empty
;
}
var
relevantNodesBuilder
=
ArrayBuilder
<
TSyntaxNode
>.
GetInstance
();
try
using
var
relevantNodesBuilderDisposer
=
ArrayBuilder
<
TSyntaxNode
>.
GetInstance
(
out
var
relevantNodesBuilder
);
// 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:
// - Most/the whole wanted Node is selected (e.g. `C [|Fun() {}|]`
// - 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
// - Token with wanted Node as direct parent is selected (e.g. IdentifierToken for LocalFunctionStatement: `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
)
{
// Every time a Node is considered an extractNodes method is called to add all nodes around the original one
// that should also be considered.
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.
//
// 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:
// - Most/the whole wanted Node is selected (e.g. `C [|Fun() {}|]`
// - 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
// - Token with wanted Node as direct parent is selected (e.g. IdentifierToken for LocalFunctionStatement: `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
)
// 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.
// We want to treat more types like expressions, e.g.: ArgumentSyntax should still trigger even if deep-in.
if
(
IsWantedTypeExpressionLike
<
TSyntaxNode
>())
{
AddRelevantNodesForSelection
(
syntaxFacts
,
root
,
selectionTrimmed
,
relevantNodesBuilder
,
cancellationToken
);
// Reason to treat Arguments (and potentially others) as Expression-like:
// https://github.com/dotnet/roslyn/pull/37295#issuecomment-516145904
await
AddNodesDeepIn
(
document
,
location
,
relevantNodesBuilder
,
cancellationToken
).
ConfigureAwait
(
false
);
}
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.
// We want to treat more types like expressions, e.g.: ArgumentSyntax should still trigger even if deep-in.
if
(
IsWantedTypeExpressionLike
<
TSyntaxNode
>())
{
// Reason to treat Arguments (and potentially others) as Expression-like:
// https://github.com/dotnet/roslyn/pull/37295#issuecomment-516145904
await
AddNodesDeepIn
(
document
,
location
,
relevantNodesBuilder
,
cancellationToken
).
ConfigureAwait
(
false
);
}
}
return
relevantNodesBuilder
.
ToImmutable
();
}
finally
{
relevantNodesBuilder
.
Free
();
}
return
relevantNodesBuilder
.
ToImmutable
();
}
private
static
bool
IsWantedTypeExpressionLike
<
TSyntaxNode
>()
where
TSyntaxNode
:
SyntaxNode
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录