|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.InlineRename;
using Microsoft.CodeAnalysis.InlineRename;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.IntegrationTest.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.VisualStudio.IntegrationTests;
using Roslyn.VisualStudio.IntegrationTests.InProcess;
using WindowsInput.Native;
using Xunit;
namespace Roslyn.VisualStudio.NewIntegrationTests.CSharp
{
[Trait(Traits.Feature, Traits.Features.Rename)]
public class CSharpRename : AbstractEditorTest
{
protected override string LanguageName => LanguageNames.CSharp;
public CSharpRename()
: base(nameof(CSharpRename))
{
}
public override async Task InitializeAsync()
{
await base.InitializeAsync();
// reset relevant global options to default values:
var globalOptions = await TestServices.Shell.GetComponentModelServiceAsync<IGlobalOptionService>(HangMitigatingCancellationToken);
globalOptions.SetGlobalOption(InlineRenameUIOptionsStorage.UseInlineAdornment, false);
globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameInComments, false);
globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameInStrings, false);
globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameOverloads, false);
globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameFile, true);
globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.PreviewChanges, false);
}
[IdeFact]
public async Task VerifyLocalVariableRename()
{
var markup = @"
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
int [|x|]$$ = 0;
[|x|] = 5;
TestMethod([|x|]);
}
static void TestMethod(int y)
{
}
}";
await using (var telemetry = await TestServices.Telemetry.EnableTestTelemetryChannelAsync(HangMitigatingCancellationToken))
{
await SetUpEditorAsync(markup, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
MarkupTestFile.GetSpans(markup, out var _, out ImmutableArray<TextSpan> renameSpans);
var tags = await TestServices.Editor.GetRenameTagsAsync(HangMitigatingCancellationToken);
var tagSpans = tags.SelectAsArray(tag => new TextSpan(tag.Span.Start, tag.Span.Length));
AssertEx.SetEqual(renameSpans, tagSpans);
await TestServices.Input.SendWithoutActivateAsync(new InputKey[] { VirtualKeyCode.VK_Y, VirtualKeyCode.RETURN }, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
int y$$ = 0;
y = 5;
TestMethod(y);
}
static void TestMethod(int y)
{
}
}", HangMitigatingCancellationToken);
await telemetry.VerifyFiredAsync(new[] { "vs/ide/vbcs/rename/inlinesession/session", "vs/ide/vbcs/rename/commitcore" }, HangMitigatingCancellationToken);
}
}
[IdeFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")]
public async Task VerifyAttributeRename()
{
var markup = @"
using System;
class [|$$ustom|]Attribute : Attribute
{
}
";
await SetUpEditorAsync(markup, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
MarkupTestFile.GetSpans(markup, out var _, out ImmutableArray<TextSpan> renameSpans);
var tags = await TestServices.Editor.GetRenameTagsAsync(HangMitigatingCancellationToken);
var tagSpans = tags.SelectAsArray(tag => new TextSpan(tag.Span.Start, tag.Span.Length));
AssertEx.SetEqual(renameSpans, tagSpans);
await TestServices.Input.SendWithoutActivateAsync(new InputKey[] { "Custom", VirtualKeyCode.RETURN }, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
using System;
class Custom$$Attribute : Attribute
{
}
", HangMitigatingCancellationToken);
}
[IdeFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")]
public async Task VerifyAttributeRenameWhileRenameClasss()
{
var markup = @"
using System;
class [|$$stom|]Attribute : Attribute
{
}
";
await SetUpEditorAsync(markup, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
MarkupTestFile.GetSpans(markup, out var _, out ImmutableArray<TextSpan> renameSpans);
var tags = await TestServices.Editor.GetRenameTagsAsync(HangMitigatingCancellationToken);
var tagSpans = tags.SelectAsArray(tag => new TextSpan(tag.Span.Start, tag.Span.Length));
AssertEx.SetEqual(renameSpans, tagSpans);
await TestServices.Input.SendWithoutActivateAsync("Custom", HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
using System;
class Custom$$Attribute : Attribute
{
}
", HangMitigatingCancellationToken);
}
[IdeFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")]
public async Task VerifyAttributeRenameWhileRenameAttribute()
{
var markup = @"
using System;
[[|$$stom|]]
class Bar
{
}
class [|stom|]Attribute : Attribute
{
}
";
await SetUpEditorAsync(markup, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
MarkupTestFile.GetSpans(markup, out _, out ImmutableArray<TextSpan> renameSpans);
var tags = await TestServices.Editor.GetRenameTagsAsync(HangMitigatingCancellationToken);
var tagSpans = tags.SelectAsArray(tag => new TextSpan(tag.Span.Start, tag.Span.Length));
AssertEx.SetEqual(renameSpans, tagSpans);
await TestServices.Input.SendWithoutActivateAsync("Custom", HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
using System;
[Custom$$]
class Bar
{
}
class CustomAttribute : Attribute
{
}
", HangMitigatingCancellationToken);
}
[IdeFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")]
public async Task VerifyAttributeRenameWhileRenameAttributeClass()
{
var markup = @"
using System;
[[|stom|]]
class Bar
{
}
class [|$$stom|]Attribute : Attribute
{
}
";
await SetUpEditorAsync(markup, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
MarkupTestFile.GetSpans(markup, out _, out ImmutableArray<TextSpan> renameSpans);
var tags = await TestServices.Editor.GetRenameTagsAsync(HangMitigatingCancellationToken);
var tagSpans = tags.SelectAsArray(tag => new TextSpan(tag.Span.Start, tag.Span.Length));
AssertEx.SetEqual(renameSpans, tagSpans);
await TestServices.Input.SendWithoutActivateAsync("Custom", HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
using System;
[Custom]
class Bar
{
}
class Custom$$Attribute : Attribute
{
}
", HangMitigatingCancellationToken);
}
[IdeFact]
public async Task VerifyLocalVariableRenameWithCommentsUpdated()
{
// "variable" is intentionally misspelled as "varixable" and "this" is misspelled as
// "thix" below to ensure we don't change instances of "x" in comments that are part of
// larger words
var markup = @"
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
/// <summary>
/// creates a varixable named [|x|] xx
/// </summary>
/// <param name=""args""></param>
static void Main(string[] args)
{
// thix varixable is named [|x|] xx
int [|x|]$$ = 0;
[|x|] = 5;
TestMethod([|x|]);
}
static void TestMethod(int y)
{
/*
* [|x|]
* xx
*/
}
}";
await SetUpEditorAsync(markup, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
await TestServices.InlineRename.ToggleIncludeCommentsAsync(HangMitigatingCancellationToken);
MarkupTestFile.GetSpans(markup, out var _, out ImmutableArray<TextSpan> renameSpans);
var tags = await TestServices.Editor.GetRenameTagsAsync(HangMitigatingCancellationToken);
var tagSpans = tags.SelectAsArray(tag => new TextSpan(tag.Span.Start, tag.Span.Length));
AssertEx.SetEqual(renameSpans, tagSpans);
await TestServices.Input.SendWithoutActivateAsync(new InputKey[] { VirtualKeyCode.VK_Y, VirtualKeyCode.RETURN }, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
/// <summary>
/// creates a varixable named y xx
/// </summary>
/// <param name=""args""></param>
static void Main(string[] args)
{
// thix varixable is named y xx
int y$$ = 0;
y = 5;
TestMethod(y);
}
static void TestMethod(int y)
{
/*
* y
* xx
*/
}
}", HangMitigatingCancellationToken);
}
[IdeFact]
public async Task VerifyLocalVariableRenameWithStringsUpdated()
{
var markup = @"
class Program
{
static void Main(string[] args)
{
int [|x|]$$ = 0;
[|x|] = 5;
var s = ""[|x|] xx [|x|]"";
var sLiteral =
@""
[|x|]
xx
[|x|]
"";
char c = 'x';
char cUnit = '\u0078';
}
}";
await SetUpEditorAsync(markup, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
await TestServices.InlineRename.ToggleIncludeStringsAsync(HangMitigatingCancellationToken);
MarkupTestFile.GetSpans(markup, out var _, out ImmutableArray<TextSpan> renameSpans);
var tags = await TestServices.Editor.GetRenameTagsAsync(HangMitigatingCancellationToken);
var tagSpans = tags.SelectAsArray(tag => new TextSpan(tag.Span.Start, tag.Span.Length));
AssertEx.SetEqual(renameSpans, tagSpans);
await TestServices.Input.SendWithoutActivateAsync(new InputKey[] { VirtualKeyCode.VK_Y, VirtualKeyCode.RETURN }, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class Program
{
static void Main(string[] args)
{
int y$$ = 0;
y = 5;
var s = ""y xx y"";
var sLiteral =
@""
y
xx
y
"";
char c = 'x';
char cUnit = '\u0078';
}
}", HangMitigatingCancellationToken);
}
[IdeFact]
public async Task VerifyOverloadsUpdated()
{
var markup = @"
interface I
{
void [|TestMethod|]$$(int y);
void [|TestMethod|](string y);
}
class B : I
{
public virtual void [|TestMethod|](int y)
{ }
public virtual void [|TestMethod|](string y)
{ }
}";
await SetUpEditorAsync(markup, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
await TestServices.InlineRename.ToggleIncludeOverloadsAsync(HangMitigatingCancellationToken);
MarkupTestFile.GetSpans(markup, out var _, out ImmutableArray<TextSpan> renameSpans);
var tags = await TestServices.Editor.GetRenameTagsAsync(HangMitigatingCancellationToken);
var tagSpans = tags.SelectAsArray(tag => new TextSpan(tag.Span.Start, tag.Span.Length));
AssertEx.SetEqual(renameSpans, tagSpans);
await TestServices.Input.SendWithoutActivateAsync(new InputKey[] { VirtualKeyCode.VK_Y, VirtualKeyCode.RETURN }, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
interface I
{
void y$$(int y);
void y(string y);
}
class B : I
{
public virtual void y(int y)
{ }
public virtual void y(string y)
{ }
}", HangMitigatingCancellationToken);
}
[IdeFact]
public async Task VerifyMultiFileRename()
{
await SetUpEditorAsync(@"
class $$Program
{
}", HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "Class2.cs", @"", cancellationToken: HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class2.cs", HangMitigatingCancellationToken);
const string class2Markup = @"
class SomeOtherClass
{
void M()
{
[|Program|] p = new [|Program|]();
}
}";
MarkupTestFile.GetSpans(class2Markup, out var code, out ImmutableArray<TextSpan> renameSpans);
await TestServices.Editor.SetTextAsync(code, HangMitigatingCancellationToken);
await TestServices.Editor.PlaceCaretAsync("Program", charsOffset: 0, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
var tags = await TestServices.Editor.GetRenameTagsAsync(HangMitigatingCancellationToken);
var tagSpans = tags.SelectAsArray(tag => new TextSpan(tag.Span.Start, tag.Span.Length));
AssertEx.SetEqual(renameSpans, tagSpans);
await TestServices.Input.SendWithoutActivateAsync(new InputKey[] { VirtualKeyCode.VK_Y, VirtualKeyCode.RETURN }, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class SomeOtherClass
{
void M()
{
y$$ p = new y();
}
}", HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class y$$
{
}", HangMitigatingCancellationToken);
}
[IdeFact]
public async Task VerifyRenameCancellation()
{
await SetUpEditorAsync(@"
class $$Program
{
}", HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "Class2.cs", @"", cancellationToken: HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class2.cs", HangMitigatingCancellationToken);
await TestServices.Editor.SetTextAsync(@"
class SomeOtherClass
{
void M()
{
Program p = new Program();
}
}", HangMitigatingCancellationToken);
await TestServices.Editor.PlaceCaretAsync("Program", charsOffset: 0, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.VK_Y, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class SomeOtherClass
{
void M()
{
y$$ p = new y();
}
}", HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class y$$
{
}", HangMitigatingCancellationToken);
await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.ESCAPE, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class Program$$
{
}", HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class2.cs", HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class SomeOtherClass
{
void M()
{
Program$$ p = new Program();
}
}", HangMitigatingCancellationToken);
}
[IdeFact]
public async Task VerifyCrossProjectRename()
{
await SetUpEditorAsync(@"
$$class RenameRocks
{
static void Main(string[] args)
{
Class2 c = null;
c.ToString();
}
}", HangMitigatingCancellationToken);
var project1 = ProjectName;
var project2 = "Project2";
await TestServices.SolutionExplorer.AddProjectAsync(project2, WellKnownProjectTemplates.ClassLibrary, LanguageName, HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.AddProjectReferenceAsync(projectName: project1, projectToReferenceName: project2, HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.AddFileAsync(project2, "Class2.cs", @"", cancellationToken: HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(project2, "Class2.cs", HangMitigatingCancellationToken);
await TestServices.Editor.SetTextAsync(@"
public class Class2 { static void Main(string [] args) { } }", HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(project1, "Class1.cs", HangMitigatingCancellationToken);
await TestServices.Editor.PlaceCaretAsync("Class2", charsOffset: 0, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
await TestServices.Input.SendWithoutActivateAsync(new InputKey[] { VirtualKeyCode.VK_Y, VirtualKeyCode.RETURN }, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class RenameRocks
{
static void Main(string[] args)
{
y$$ c = null;
c.ToString();
}
}", HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(project2, "y.cs", HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
public class y { static void Main(string [] args) { } }$$", cancellationToken: HangMitigatingCancellationToken);
}
[IdeFact]
public async Task VerifyRenameUndo()
{
await VerifyCrossProjectRename();
await TestServices.Input.SendWithoutActivateAsync((VirtualKeyCode.VK_Z, VirtualKeyCode.CONTROL), HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
public class Class2 { static void Main(string [] args) { } }$$", HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class RenameRocks
{
static void Main(string[] args)
{
Class2$$ c = null;
c.ToString();
}
}", HangMitigatingCancellationToken);
}
[IdeFact]
public async Task VerifyRenameInStandaloneFiles()
{
await TestServices.SolutionExplorer.CloseSolutionAsync(HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.AddStandaloneFileAsync("StandaloneFile1.cs", HangMitigatingCancellationToken);
await TestServices.Editor.SetTextAsync(@"
class Program
{
void Goo()
{
var ids = 1;
ids = 2;
}
}", HangMitigatingCancellationToken);
await TestServices.Editor.PlaceCaretAsync("ids", charsOffset: 0, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
await TestServices.Input.SendWithoutActivateAsync(new InputKey[] { VirtualKeyCode.VK_Y, VirtualKeyCode.RETURN }, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(@"
class Program
{
void Goo()
{
var y$$ = 1;
y = 2;
}
}", HangMitigatingCancellationToken);
}
[IdeFact, WorkItem("https://github.com/dotnet/roslyn/issues/39617")]
public async Task VerifyRenameCaseChange()
{
await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "Program.cs",
@"
class Program
{
static void Main(string[] args)
{
}
}", cancellationToken: HangMitigatingCancellationToken);
await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Program.cs", HangMitigatingCancellationToken);
await TestServices.Editor.PlaceCaretAsync("Program", charsOffset: 0, HangMitigatingCancellationToken);
await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken);
await TestServices.Input.SendWithoutActivateAsync(new InputKey[] { VirtualKeyCode.HOME, VirtualKeyCode.DELETE, VirtualKeyCode.VK_P, VirtualKeyCode.RETURN }, HangMitigatingCancellationToken);
await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken);
await TestServices.EditorVerifier.TextEqualsAsync(
@"
class p$$rogram
{
static void Main(string[] args)
{
}
}", HangMitigatingCancellationToken);
}
}
}
|