diff --git a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs index 235c84df71f06033d5de4ff3872887c1152ef83a..e898d6929cce48aed1d11afdafdb24a675e07bbc 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs @@ -4616,6 +4616,173 @@ void M() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] + [WorkItem(10123, "https://github.com/dotnet/roslyn/issues/10123")] + public async Task TestSimpleParameterName() + { + await TestInRegularAndScriptAsync( +@" +class C +{ + void M(int a) + { + System.Console.Write([|a|]); + } +}", +@" +class C +{ + void M(int a) + { + int {|Rename:a1|} = a; + System.Console.Write(a1); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] + [WorkItem(10123, "https://github.com/dotnet/roslyn/issues/10123")] + public async Task TestSimpleParamterName_EmptySelection() + { + await TestMissingAsync( +@"class C +{ + void M(int a) + { + System.Console.Write([||]a); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] + [WorkItem(10123, "https://github.com/dotnet/roslyn/issues/10123")] + public async Task TestSimpleParamterName_SmallSelection() + { + await TestMissingAsync( +@"class C +{ + void M(int parameter) + { + System.Console.Write([|par|]ameter); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] + [WorkItem(10123, "https://github.com/dotnet/roslyn/issues/10123")] + public async Task TestFieldName_QualifiedWithThis() + { + await TestInRegularAndScriptAsync( +@" +class C +{ + int a; + void M() + { + System.Console.Write([|this.a|]); + } +}", +@" +class C +{ + int a; + void M() + { + int {|Rename:a1|} = this.a; + System.Console.Write(a1); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] + [WorkItem(10123, "https://github.com/dotnet/roslyn/issues/10123")] + public async Task TestFieldName_QualifiedWithType() + { + await TestInRegularAndScriptAsync( +@" +class C +{ + static int a; + void M() + { + System.Console.Write([|C.a|]); + } +}", +@" +class C +{ + static int a; + void M() + { + int {|Rename:a1|} = C.a; + System.Console.Write(a1); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] + [WorkItem(10123, "https://github.com/dotnet/roslyn/issues/10123")] + public async Task TestFieldName_QualifiedWithType_TinySelection1() + { + await TestMissingAsync( +@" +class C +{ + static int a; + void M() + { + System.Console.Write(C[|.|]a); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] + [WorkItem(10123, "https://github.com/dotnet/roslyn/issues/10123")] + public async Task TestFieldName_QualifiedWithType_TinySelection2() + { + await TestMissingAsync( +@" +class C +{ + static int a; + void M() + { + System.Console.Write([|C.|]a); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] + [WorkItem(10123, "https://github.com/dotnet/roslyn/issues/10123")] + public async Task TestFieldName_QualifiedWithType_TinySelection3() + { + await TestMissingAsync( +@" +class C +{ + static int a; + void M() + { + System.Console.Write(C.[|a|]); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] + [WorkItem(10123, "https://github.com/dotnet/roslyn/issues/10123")] + public async Task TestFieldName_QualifiedWithType_EmptySelection() + { + await TestMissingAsync( +@"class C +{ + static int a; + void M() + { + System.Console.Write(C.[||]a); + } +}"); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)] public async Task TestIntroduceLocalInCallRefExpression() { diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb index 32e13d205d1220069f9938f9127fc99140b90cbe..b43cb5ed648ed1575252bf1566f04d47b69abb7d 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb @@ -2953,6 +2953,72 @@ structure TextSpan end structure") End Function + + + Public Async Function TestSimpleParameterName() As Task + Dim source = "Module Program + Sub Main(x As Integer) + Goo([|x|]) + End Sub +End Module" + Dim expected = "Module Program + Sub Main(x As Integer) + Dim {|Rename:x1|} As Integer = x + Goo(x1) + End Sub +End Module" + Await TestInRegularAndScriptAsync(source, expected) + End Function + + + + Public Async Function TestSimpleParameterName_EmptySelection() As Task + Dim source = "Module Program + Sub Main(x As Integer) + Goo([||]x) + End Sub +End Module" + Await TestMissingAsync(source) + End Function + + + + Public Async Function TestFieldName_QualifiedWithMe() As Task + Dim source = "Module Program + Dim x As Integer + Sub Main() + Goo([|x|]) + End Sub +End Module" + Dim expected = "Module Program + Dim x As Integer + Sub Main() + Dim {|Rename:x1|} As Integer = x + Goo(x1) + End Sub +End Module" + Await TestInRegularAndScriptAsync(source, expected) + End Function + + + + Public Async Function TestFieldName_QualifiedWithType() As Task + Dim source = "Module Program + Shared Dim x As Integer + Sub Main() + Goo([|Program.x|]) + End Sub +End Module" + Dim expected = "Module Program + Shared Dim x As Integer + Sub Main() + Dim {|Rename:x1|} As Integer = Program.x + Goo(x1) + End Sub +End Module" + Await TestInRegularAndScriptAsync(source, expected) + End Function + Public Async Function TestInAttribute() As Task diff --git a/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService.cs b/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService.cs index 221e717f83a32abd6994589bc9d2500fd7f60fcf..dfa866f410c0a8a6eef8a3db5f6b7b26b93d3d1e 100644 --- a/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService.cs +++ b/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.IntroduceVariable { [ExportLanguageService(typeof(IIntroduceVariableService), LanguageNames.CSharp), Shared] internal partial class CSharpIntroduceVariableService : - AbstractIntroduceVariableService + AbstractIntroduceVariableService { protected override bool IsInNonFirstQueryClause(ExpressionSyntax expression) { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs index 5786f88c6a763f4cf11ce9bc32352f92bf75d033..34a61ba9183450c3fa22a05e20518025d8c2e86e 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { internal abstract class AbstractIntroduceVariableCodeAction : CodeAction { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs index ae03a34fbb85be572230028e71d400352823443c..7d0a89d96b51936947cd861622c0a1edbb44d4ed 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs @@ -3,7 +3,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { private class IntroduceVariableCodeAction : AbstractIntroduceVariableCodeAction { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs index 7f91b16bbb44585a999ce889574cd354aa68a6d5..f0f16e6cbaa029655e0d5cb11c279f7d558d5b52 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { private class IntroduceVariableAllOccurrenceCodeAction : AbstractIntroduceVariableCodeAction { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State.cs index 18b9b6e8313522e652ef3c8dbf7a4a6e2df50fc4..d5a0c1f5c0badb7a75c6f3e0ab9e0a1856b9aec7 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { private partial class State { @@ -82,7 +82,7 @@ public State(TService service, SemanticDocument document) return false; } - if (!CanIntroduceVariable(cancellationToken)) + if (!CanIntroduceVariable(textSpan.IsEmpty, cancellationToken)) { return false; } @@ -229,6 +229,7 @@ private TExpressionSyntax GetExpressionUnderSpan(SyntaxTree tree, TextSpan textS } private bool CanIntroduceVariable( + bool isSpanEmpty, CancellationToken cancellationToken) { if (!_service.CanIntroduceVariableFor(this.Expression)) @@ -236,8 +237,15 @@ private TExpressionSyntax GetExpressionUnderSpan(SyntaxTree tree, TextSpan textS return false; } - if (this.Expression is TTypeSyntax) + if (isSpanEmpty && this.Expression is TNameSyntax) { + // to extract a name, you must have a selection (this avoids making the refactoring too noisy) + return false; + } + + if (this.Expression is TTypeSyntax && !(this.Expression is TNameSyntax)) + { + // name syntax can introduce variables, but not other type syntaxes return false; } diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs index 477aa58de4bde19b5d41ff3cf644e95d7a1d0eff..172db039334a7e0dd9bdb597cd54795975e5b37f 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs @@ -4,7 +4,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { private partial class State { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs index 47bbd4e8504f4b54b8dc476c1380230c6a823107..d7481a57bcf17dcba4dfb797a3ef6d8b11ee2768 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs @@ -6,7 +6,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { private partial class State { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs index cb9c199d4f3a61d449d79e16ea1471b8bc11fa4a..16b087b4c1b4ec2ad37a294e48af1e076e9a2a50 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs @@ -7,7 +7,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { private partial class State { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs index 61898fa0446c476ab0cc846f8dc17f629d491a84..027b70328951f53098887c0fb42ee9fd9b3e38e9 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs @@ -7,7 +7,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { private partial class State { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs index c79015ce917558aa873109cda2357d69fff32335..d583abfae84437b0ea057fb07b0628f922d876ce 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs @@ -6,7 +6,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { private partial class State { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs index cf75dbe7dac26259bafa30d69674e6187b6c664f..13479919a292ce361084b5a5d34b686cc47a96cc 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs @@ -6,7 +6,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal partial class AbstractIntroduceVariableService + internal partial class AbstractIntroduceVariableService { private partial class State { diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs index 55c1b79ece44e0c371f27c3f3a323ce00cb5ffef..aa4ecc6e92034a4b19541ed253d2a2e8ef969995 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs @@ -18,12 +18,13 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable { - internal abstract partial class AbstractIntroduceVariableService : IIntroduceVariableService - where TService : AbstractIntroduceVariableService + internal abstract partial class AbstractIntroduceVariableService : IIntroduceVariableService + where TService : AbstractIntroduceVariableService where TExpressionSyntax : SyntaxNode where TTypeSyntax : TExpressionSyntax where TTypeDeclarationSyntax : SyntaxNode where TQueryExpressionSyntax : TExpressionSyntax + where TNameSyntax : TTypeSyntax { protected abstract bool IsInNonFirstQueryClause(TExpressionSyntax expression); protected abstract bool IsInFieldInitializer(TExpressionSyntax expression); diff --git a/src/Features/VisualBasic/Portable/IntroduceVariable/VisualBasicIntroduceVariableService.vb b/src/Features/VisualBasic/Portable/IntroduceVariable/VisualBasicIntroduceVariableService.vb index e0b80d85ea824be2ad409e5562fb50577bd4d267..a44a164b2bdd8ff315292c8fff12516805bd7dd8 100644 --- a/src/Features/VisualBasic/Portable/IntroduceVariable/VisualBasicIntroduceVariableService.vb +++ b/src/Features/VisualBasic/Portable/IntroduceVariable/VisualBasicIntroduceVariableService.vb @@ -10,8 +10,8 @@ Imports System.Composition Namespace Microsoft.CodeAnalysis.VisualBasic.IntroduceVariable - Friend Class VisualBasicIntroduceVariableService - Inherits AbstractIntroduceVariableService(Of VisualBasicIntroduceVariableService, ExpressionSyntax, TypeSyntax, TypeBlockSyntax, QueryExpressionSyntax) + Partial Friend Class VisualBasicIntroduceVariableService + Inherits AbstractIntroduceVariableService(Of VisualBasicIntroduceVariableService, ExpressionSyntax, TypeSyntax, TypeBlockSyntax, QueryExpressionSyntax, NameSyntax) Protected Overrides Function GetContainingExecutableBlocks(expression As ExpressionSyntax) As IEnumerable(Of SyntaxNode) Return expression.GetContainingExecutableBlocks()