Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
1d43e8be
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,发现更多精彩内容 >>
提交
1d43e8be
编写于
7月 11, 2019
作者:
P
Petr Houska
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Enabled RefactoringHelpers to handle location in whitespace.
上级
87ca1880
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
138 addition
and
26 deletion
+138
-26
src/EditorFeatures/CSharpTest/RefactoringHelpers/RefactoringHelpersTests.cs
.../CSharpTest/RefactoringHelpers/RefactoringHelpersTests.cs
+69
-7
src/Features/CSharp/Portable/CodeRefactorings/CSharpRefactoringHelpersService.cs
...table/CodeRefactorings/CSharpRefactoringHelpersService.cs
+1
-1
src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs
...ble/CodeRefactorings/AbstractRefactoringHelpersService.cs
+67
-17
src/Features/VisualBasic/Portable/CodeRefactorings/VisualBasicRefactoringHelpersService.vb
.../CodeRefactorings/VisualBasicRefactoringHelpersService.vb
+1
-1
未找到文件。
src/EditorFeatures/CSharpTest/RefactoringHelpers/RefactoringHelpersTests.cs
浏览文件 @
1d43e8be
...
...
@@ -166,18 +166,20 @@ C LocalFunction(C c)
[
Fact
]
[
WorkItem
(
35525
,
"https://github.com/dotnet/roslyn/issues/35525"
)]
public
async
Task
TestMissing
LocationAfter
()
public
async
Task
TestMissing
InTooFarBeforeInWhitespace
()
{
var
testText
=
@"
class C
{
void M()
{
[||]
C LocalFunction(C c)
{
return null;
}
[||]
}
}"
;
await
TestMissingAsync
<
LocalFunctionStatementSyntax
>(
testText
);
...
...
@@ -185,18 +187,20 @@ C LocalFunction(C c)
[
Fact
]
[
WorkItem
(
35525
,
"https://github.com/dotnet/roslyn/issues/35525"
)]
public
async
Task
TestMissing
LocationBefore
()
public
async
Task
TestMissing
InWhiteSpaceOnLineWithDifferentStatement
()
{
var
testText
=
@"
class C
{
void M()
{
[||]
var a = null; [||]
C LocalFunction(C c)
{
return null;
}
}
}"
;
await
TestMissingAsync
<
LocalFunctionStatementSyntax
>(
testText
);
...
...
@@ -204,23 +208,61 @@ C LocalFunction(C c)
[
Fact
]
[
WorkItem
(
35525
,
"https://github.com/dotnet/roslyn/issues/35525"
)]
public
async
Task
TestMissingLocationBefore2
()
public
async
Task
TestBeforeInWhitespace1
()
{
var
testText
=
@"
class C
{
void M()
{
[||]//Test comment
{|result:C LocalFunction(C c)
{
return null;
}|}
}
}"
;
await
TestAsync
<
LocalFunctionStatementSyntax
>(
testText
);
}
[
Fact
]
[
WorkItem
(
35525
,
"https://github.com/dotnet/roslyn/issues/35525"
)]
public
async
Task
TestBeforeInWhitespace2
()
{
var
testText
=
@"
class C
{
void M()
{
var a = null;
[||] {|result:C LocalFunction(C c)
{
return null;
}|}
}
}"
;
await
TestAsync
<
LocalFunctionStatementSyntax
>(
testText
);
}
[
Fact
]
[
WorkItem
(
35525
,
"https://github.com/dotnet/roslyn/issues/35525"
)]
public
async
Task
TestMissingInNextTokensLeadingTrivia
()
{
var
testText
=
@"
class C
{
void M()
{
var a = new object();[||]
C LocalFunction(C c)
{
return null;
}
[||]
}
}"
;
await
TestMissingAsync
<
LocalFunctionStatementSyntax
>(
testText
);
}
#
endregion
#
region
Selections
...
...
@@ -430,6 +472,26 @@ C LocalFunction(C c)
await
TestMissingAsync
<
LocalFunctionStatementSyntax
>(
testText
);
}
[
Fact
]
[
WorkItem
(
35525
,
"https://github.com/dotnet/roslyn/issues/35525"
)]
public
async
Task
TestMissingSelectionBefore
()
{
var
testText
=
@"
class C
{
void M()
{
[| |]C LocalFunction(C c)
{
return null;
}
var a = new object();
}
}"
;
await
TestMissingAsync
<
LocalFunctionStatementSyntax
>(
testText
);
}
#
endregion
#
region
Attributes
...
...
src/Features/CSharp/Portable/CodeRefactorings/CSharpRefactoringHelpersService.cs
浏览文件 @
1d43e8be
...
...
@@ -11,7 +11,7 @@
namespace
Microsoft.CodeAnalysis.CSharp.CodeRefactorings
{
[
ExportLanguageService
(
typeof
(
IRefactoringHelpersService
),
LanguageNames
.
CSharp
),
Shared
]
internal
class
CSharpRefactoringHelpersService
:
AbstractRefactoringHelpersService
<
PropertyDeclarationSyntax
,
ParameterSyntax
,
MethodDeclarationSyntax
>
internal
class
CSharpRefactoringHelpersService
:
AbstractRefactoringHelpersService
<
PropertyDeclarationSyntax
,
ParameterSyntax
,
MethodDeclarationSyntax
,
LocalDeclarationStatementSyntax
>
{
protected
override
IEnumerable
<
SyntaxNode
>
ExtractNodeOfHeader
(
SyntaxNode
node
,
ISyntaxFactsService
syntaxFacts
)
{
...
...
src/Features/Core/Portable/CodeRefactorings/AbstractRefactoringHelpersService.cs
浏览文件 @
1d43e8be
...
...
@@ -10,10 +10,11 @@
namespace
Microsoft.CodeAnalysis.CodeRefactorings
{
internal
abstract
class
AbstractRefactoringHelpersService
<
TPropertyDeclaration
,
TParameter
,
TMethodDeclaration
>
:
IRefactoringHelpersService
internal
abstract
class
AbstractRefactoringHelpersService
<
TPropertyDeclaration
,
TParameter
,
TMethodDeclaration
,
TLocalDeclaration
>
:
IRefactoringHelpersService
where
TPropertyDeclaration
:
SyntaxNode
where
TParameter
:
SyntaxNode
where
TMethodDeclaration
:
SyntaxNode
where
TLocalDeclaration
:
SyntaxNode
{
public
async
Task
<
TSyntaxNode
>
TryGetSelectedNodeAsync
<
TSyntaxNode
>(
Document
document
,
TextSpan
selection
,
CancellationToken
cancellationToken
)
where
TSyntaxNode
:
SyntaxNode
...
...
@@ -152,7 +153,7 @@ protected async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, Text
// - 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
// - 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
...
...
@@ -160,6 +161,8 @@ protected async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, Text
// 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.
if
(!
selectionRaw
.
IsEmpty
)
{
return
null
;
...
...
@@ -174,6 +177,54 @@ protected async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, Text
?
tokenOnLocation
:
default
;
// A token can be to the left only when there's either no tokenDirectlyToRightOrIn or there's one
// directly starting at current location. Otherwise (otherwise tokenToRightOrIn is also left from location, e.g: `tok[||]enToRightOrIn`)
var
tokenToLeft
=
default
(
SyntaxToken
);
if
(
tokenToRightOrIn
==
default
||
tokenToRightOrIn
.
Span
.
Start
==
location
)
{
var
tokenPreLocation
=
(
tokenOnLocation
.
Span
.
End
==
location
)
?
tokenOnLocation
:
tokenOnLocation
.
GetPreviousToken
();
tokenToLeft
=
(
tokenPreLocation
.
Span
.
End
==
location
)
?
tokenPreLocation
:
default
;
}
// If both tokens directly to left & right are empty -> we're somewhere in the middle of whitespace.
// Since there wouldn't be (m)any other refactorings we can try to offer at least the ones for (semantically)
// closest token/Node. Thus, we move the location to the token in whose `.FullSpan` the original location was.
if
(
tokenToLeft
==
default
&&
tokenToRightOrIn
==
default
)
{
var
text
=
await
document
.
GetTextAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
sourceText
=
await
document
.
GetTextAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
// assume non-trivia token can't span multiple lines
var
tokenLine
=
sourceText
.
Lines
.
GetLineFromPosition
(
tokenOnLocation
.
Span
.
Start
);
var
locationLine
=
sourceText
.
Lines
.
GetLineFromPosition
(
location
);
// Change location to nearest token only if the token is off by one line or less
if
(
Math
.
Abs
(
tokenLine
.
LineNumber
-
locationLine
.
LineNumber
)
<=
1
)
{
// Note: being a line below a tokenOnLocation is impossible in current model as whitespace
// trailing trivia ends on new line. Which is fine because if you're a line _after_ some node
// you usually don't want refactorings for what's above you.
// tokenOnLocation: token in whose trivia location is at
if
(
tokenOnLocation
.
Span
.
Start
>=
location
)
{
tokenToRightOrIn
=
tokenOnLocation
;
location
=
tokenToRightOrIn
.
Span
.
Start
;
}
else
{
tokenToLeft
=
tokenOnLocation
;
location
=
tokenToLeft
.
Span
.
End
;
}
}
}
if
(
tokenToRightOrIn
!=
default
)
{
var
rightNode
=
tokenOnLocation
.
Parent
;
...
...
@@ -213,21 +264,6 @@ protected async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, Text
while
(
true
);
}
// if the location is inside tokenToRightOrIn -> no Token can be to Left (tokenToRightOrIn is also left from location, e.g: `tok[||]enToRightOrIn`)
if
(
tokenToRightOrIn
!=
default
&&
tokenToRightOrIn
.
Span
.
Start
!=
location
)
{
return
null
;
}
// Token to left: a token whose span ends on current location
var
tokenPreLocation
=
(
tokenOnLocation
.
Span
.
End
==
location
)
?
tokenOnLocation
:
tokenOnLocation
.
GetPreviousToken
();
var
tokenToLeft
=
(
tokenPreLocation
.
Span
.
End
==
location
)
?
tokenPreLocation
:
default
;
if
(
tokenToLeft
!=
default
)
{
var
leftNode
=
tokenToLeft
.
Parent
;
...
...
@@ -332,6 +368,10 @@ protected virtual IEnumerable<SyntaxNode> DefaultNodeExtractor(SyntaxNode node,
foreach
(
var
headerNode
in
ExtractNodeOfHeader
(
node
,
syntaxFacts
))
{
yield
return
headerNode
;
foreach
(
var
extractedNode
in
ExtractNodeSimple
(
headerNode
,
syntaxFacts
))
{
yield
return
extractedNode
;
}
}
}
}
...
...
@@ -362,6 +402,16 @@ protected virtual IEnumerable<SyntaxNode> ExtractNodeSimple(SyntaxNode node, ISy
}
}
// var `a = b`;
// -> `var a = b`;
if
(
node
.
Parent
!=
null
&&
syntaxFacts
.
IsLocalDeclarationStatement
(
node
.
Parent
))
{
if
(
syntaxFacts
.
GetVariablesOfLocalDeclarationStatement
(
node
.
Parent
).
Count
==
1
)
{
yield
return
node
.
Parent
;
}
}
// `a = b;`
// -> `b`
if
(
syntaxFacts
.
IsSimpleAssignmentStatement
(
node
))
...
...
src/Features/VisualBasic/Portable/CodeRefactorings/VisualBasicRefactoringHelpersService.vb
浏览文件 @
1d43e8be
...
...
@@ -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
PropertyStatementSyntax
,
ParameterSyntax
,
MethodStatementSyntax
)
Inherits
AbstractRefactoringHelpersService
(
Of
PropertyStatementSyntax
,
ParameterSyntax
,
MethodStatementSyntax
,
LocalDeclarationStatementSyntax
)
Protected
Overrides
Iterator
Function
ExtractNodeSimple
(
node
As
SyntaxNode
,
syntaxFacts
As
ISyntaxFactsService
)
As
IEnumerable
(
Of
SyntaxNode
)
For
Each
baseExtraction
In
MyBase
.
ExtractNodeSimple
(
node
,
syntaxFacts
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录