From 444eb111536b1e216a5ada30013967188b8ae6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 14 Jun 2013 10:46:40 +0200 Subject: [PATCH] Implemented basic rename feature. --- .../Resolver/FindReferences.cs | 70 ++++++++++++- .../Analysis/SymbolCollectorTests.cs | 41 ++++++++ .../CSharp/Resolver/FindReferencesTest.cs | 97 +++++++++++++++++++ .../ICSharpCode.NRefactory.Tests.csproj | 4 +- ...{MemberCollector.cs => SymbolCollector.cs} | 6 +- .../ICSharpCode.NRefactory.csproj | 3 +- 6 files changed, 211 insertions(+), 10 deletions(-) create mode 100644 ICSharpCode.NRefactory.Tests/Analysis/SymbolCollectorTests.cs rename ICSharpCode.NRefactory/Analysis/{MemberCollector.cs => SymbolCollector.cs} (88%) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs b/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs index 495d2f91e6..a291036f54 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs @@ -473,10 +473,76 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region RenameReferencesInFile + + AstNode GetNodeToReplace(AstNode node) + { + if (node is ConstructorInitializer) + return null; + if (node is ObjectCreateExpression) + node = ((ObjectCreateExpression)node).Type; + + if (node is InvocationExpression) + node = ((InvocationExpression)node).Target; + + if (node is MemberReferenceExpression) + node = ((MemberReferenceExpression)node).MemberNameToken; + + if (node is SimpleType) + node = ((SimpleType)node).IdentifierToken; + + if (node is MemberType) + node = ((MemberType)node).MemberNameToken; + + if (node is NamespaceDeclaration) { +// var nsd = ((NamespaceDeclaration)node); +// node = nsd.Identifiers.LastOrDefault (n => n.Name == memberName) ?? nsd.Identifiers.FirstOrDefault (); +// if (node == null) + return null; + } + + if (node is TypeDeclaration) + node = ((TypeDeclaration)node).NameToken; + if (node is DelegateDeclaration) + node = ((DelegateDeclaration)node).NameToken; + + if (node is EntityDeclaration) + node = ((EntityDeclaration)node).NameToken; + + if (node is ParameterDeclaration) + node = ((ParameterDeclaration)node).NameToken; + if (node is ConstructorDeclaration) + node = ((ConstructorDeclaration)node).NameToken; + if (node is DestructorDeclaration) + node = ((DestructorDeclaration)node).NameToken; + if (node is NamedArgumentExpression) + node = ((NamedArgumentExpression)node).NameToken; + if (node is NamedExpression) + node = ((NamedExpression)node).NameToken; + if (node is VariableInitializer) + node = ((VariableInitializer)node).NameToken; + + if (node is IdentifierExpression) { + node = ((IdentifierExpression)node).IdentifierToken; + } + return node; + } + public void RenameReferencesInFile(IList searchScopes, string newName, CSharpAstResolver resolver, - Action callback, Action errorCallback, CancellationToken cancellationToken) + Action callback, Action errorCallback, CancellationToken cancellationToken = default (CancellationToken)) { - throw new NotImplementedException(); + WholeVirtualSlot = true; + FindReferencesInFile( + searchScopes, + resolver, + delegate(AstNode astNode, ResolveResult result) { + var nodeToReplace = GetNodeToReplace(astNode); + if (nodeToReplace == null) { + errorCallback (new Error (ErrorType.Error, "no node to replace found.")); + return; + } + callback (new RenameCallbackArguments(nodeToReplace, Identifier.Create(newName))); + }, + cancellationToken); } #endregion diff --git a/ICSharpCode.NRefactory.Tests/Analysis/SymbolCollectorTests.cs b/ICSharpCode.NRefactory.Tests/Analysis/SymbolCollectorTests.cs new file mode 100644 index 0000000000..7675c03ec0 --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/Analysis/SymbolCollectorTests.cs @@ -0,0 +1,41 @@ +// +// SymbolCollectorTests.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Linq; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.TypeSystem; +using NUnit.Framework; +using ICSharpCode.NRefactory.CSharp.CodeCompletion; + +namespace ICSharpCode.NRefactory.Analysis +{ + [TestFixture] + public class SymbolCollectorTests + { + + } +} + diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs index d01c48826b..08bc38a260 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs @@ -24,6 +24,9 @@ using System.Threading; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.TypeSystem; using NUnit.Framework; +using ICSharpCode.NRefactory.Analysis; +using System.Text; +using ICSharpCode.NRefactory.Editor; namespace ICSharpCode.NRefactory.CSharp.Resolver { @@ -383,5 +386,99 @@ namespace Foo Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 12 && r is SimpleType)); } #endregion + + #region Rename + + ISymbol GetSymbol (string reflectionName) + { + Stack typeStack = new Stack(compilation.MainAssembly.TopLevelTypeDefinitions); + while (typeStack.Count > 0) { + var cur = typeStack.Pop(); + if (cur.ReflectionName == reflectionName) + return cur; + foreach (var member in cur.Members) + if (member.ReflectionName == reflectionName) + return member; + foreach (var nested in cur.NestedTypes) { + typeStack.Push(nested); + } + } + return null; + } + + IList Rename(string fullyQualifiedName, string newName, bool includeOverloads) + { + var sym = GetSymbol(fullyQualifiedName); + Assert.NotNull(sym); + var graph = new TypeGraph(compilation.Assemblies); + + var scopes = findReferences.GetSearchScopes(SymbolCollector.GetRelatedSymbols(graph, sym, includeOverloads)); + List result = new List(); + + findReferences.RenameReferencesInFile( + scopes, + newName, + new CSharpAstResolver(compilation, syntaxTree, unresolvedFile), + delegate(RenameCallbackArguments obj) { + result.Add (obj.NodeToReplace); + }, + delegate(Error obj) { + + }); + return result; + } + + void TestRename(string code, string symbolName) + { + StringBuilder sb = new StringBuilder(); + List offsets = new List(); + foreach (var ch in code) { + if (ch == '$') { + offsets.Add(sb.Length); + continue; + } + sb.Append(ch); + } + Init(sb.ToString ()); + var doc = new ReadOnlyDocument(sb.ToString ()); + var result = Rename(symbolName, "x", false); + Assert.AreEqual(offsets.Count, result.Count); + + result.Select(r => doc.GetOffset (r.StartLocation)).SequenceEqual(offsets); + } + + [Test] + public void TestSimpleRename () + { + TestRename (@"using System; +class $Test { + $Test test; +}", "Test"); + } + + + [Test] + public void TestOverride () + { + TestRename(@"using System; +class Test { + public virtual int $Foo { get; set; } +} + +class Test2 : Test { + public override int $Foo { get; set; } +} + +class Test3 : Test { + public override int $Foo { get; set; } + public FindReferencesTest () + { + $Foo = 4; + } +} +", "Test.Foo"); + } + + #endregion } } diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index 1b40722578..8bf7a1b19a 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -417,6 +417,7 @@ + @@ -439,9 +440,6 @@ False - - - diff --git a/ICSharpCode.NRefactory/Analysis/MemberCollector.cs b/ICSharpCode.NRefactory/Analysis/SymbolCollector.cs similarity index 88% rename from ICSharpCode.NRefactory/Analysis/MemberCollector.cs rename to ICSharpCode.NRefactory/Analysis/SymbolCollector.cs index d0fcc40bbe..186f20488b 100644 --- a/ICSharpCode.NRefactory/Analysis/MemberCollector.cs +++ b/ICSharpCode.NRefactory/Analysis/SymbolCollector.cs @@ -22,11 +22,11 @@ using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.Analysis { - public class MemberCollector + public class SymbolCollector { - public static IEnumerable GetRelatedMembers(TypeGraph g, IMember m, bool includeOverloads) + public static IEnumerable GetRelatedSymbols(TypeGraph g, ISymbol m, bool includeOverloads) { - throw new NotImplementedException(); + yield return m; } } } diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 00041d86d7..2df94728d8 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -83,7 +83,6 @@ - @@ -279,10 +278,10 @@ + -