// // Rename.cs // // Author: // Mike Krüger // // Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; using MonoDevelop.Core; using Mono.TextEditor; using System.Text; using MonoDevelop.Ide; using System.Linq; using Mono.TextEditor.PopupWindow; using MonoDevelop.Ide.FindInFiles; using MonoDevelop.Ide.ProgressMonitoring; using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; using MonoDevelop.Core.ProgressMonitoring; using MonoDevelop.Ide.Gui; namespace MonoDevelop.Refactoring.Rename { public class RenameRefactoring : RefactoringOperation { public override string AccelKey { get { var key = IdeApp.CommandService.GetCommandInfo (MonoDevelop.Ide.Commands.EditCommands.Rename).AccelKey; return key == null ? null : key.Replace ("dead_circumflex", "^"); } } public RenameRefactoring () { Name = "Rename"; } public override bool IsValid (RefactoringOptions options) { if (options.SelectedItem is IVariable || options.SelectedItem is IParameter) return true; if (options.SelectedItem is INamespace) { var ns = (INamespace)options.SelectedItem; return ns.Types.Any (type => !string.IsNullOrEmpty (type.Region.FileName)); } if (options.SelectedItem is ITypeDefinition) return !string.IsNullOrEmpty (((ITypeDefinition)options.SelectedItem).Region.FileName); if (options.SelectedItem is IType && ((IType)options.SelectedItem).Kind == TypeKind.TypeParameter) return !string.IsNullOrEmpty (((ITypeParameter)options.SelectedItem).Region.FileName); var member = options.SelectedItem as IMember; if (member != null) { if (member.SymbolKind == SymbolKind.Operator) return false; var cls = member.DeclaringTypeDefinition; return cls != null; } return false; } public static void Rename (IEntity entity, string newName) { if (newName == null) { var options = new RefactoringOptions () { SelectedItem = entity }; new RenameRefactoring ().Run (options); return; } using (var monitor = new ProgressMonitor ()) { var col = ReferenceFinder.FindReferences (entity, true, monitor); List result = new List (); foreach (var memberRef in col) { var change = new TextReplaceChange (); change.FileName = memberRef.FileName; change.Offset = memberRef.Offset; change.RemovedChars = memberRef.Length; change.InsertedText = newName; change.Description = string.Format (GettextCatalog.GetString ("Replace '{0}' with '{1}'"), memberRef.GetName (), newName); result.Add (change); } if (result.Count > 0) { RefactoringService.AcceptChanges (monitor, result); } } } public static void RenameVariable (IVariable variable, string newName) { using (var monitor = new ProgressMonitor ()) { var col = ReferenceFinder.FindReferences (variable, true, monitor); List result = new List (); foreach (var memberRef in col) { var change = new TextReplaceChange (); change.FileName = memberRef.FileName; change.Offset = memberRef.Offset; change.RemovedChars = memberRef.Length; change.InsertedText = newName; change.Description = string.Format (GettextCatalog.GetString ("Replace '{0}' with '{1}'"), memberRef.GetName (), newName); result.Add (change); } if (result.Count > 0) { RefactoringService.AcceptChanges (monitor, result); } } } public static void RenameTypeParameter (ITypeParameter typeParameter, string newName) { if (newName == null) { var options = new RefactoringOptions () { SelectedItem = typeParameter }; new RenameRefactoring ().Run (options); return; } using (var monitor = new ProgressMonitor ()) { var col = ReferenceFinder.FindReferences (typeParameter, true, monitor); List result = new List (); foreach (var memberRef in col) { var change = new TextReplaceChange (); change.FileName = memberRef.FileName; change.Offset = memberRef.Offset; change.RemovedChars = memberRef.Length; change.InsertedText = newName; change.Description = string.Format (GettextCatalog.GetString ("Replace '{0}' with '{1}'"), memberRef.GetName (), newName); result.Add (change); } if (result.Count > 0) { RefactoringService.AcceptChanges (monitor, result); } } } public static void RenameNamespace (INamespace ns, string newName) { using (var monitor = new ProgressMonitor ()) { var col = ReferenceFinder.FindReferences (ns, true, monitor); List result = new List (); foreach (var memberRef in col) { var change = new TextReplaceChange (); change.FileName = memberRef.FileName; change.Offset = memberRef.Offset; change.RemovedChars = memberRef.Length; change.InsertedText = newName; change.Description = string.Format (GettextCatalog.GetString ("Replace '{0}' with '{1}'"), memberRef.GetName (), newName); result.Add (change); } if (result.Count > 0) { RefactoringService.AcceptChanges (monitor, result); } } } public override string GetMenuDescription (RefactoringOptions options) { return IdeApp.CommandService.GetCommandInfo (MonoDevelop.Ide.Commands.EditCommands.Rename).Text; } public override void Run (RefactoringOptions options) { if (options.SelectedItem is IVariable) { var field = options.SelectedItem as IField; if (field != null && (field.Accessibility != Accessibility.Private || field.DeclaringTypeDefinition != null && field.DeclaringTypeDefinition.Parts.Count > 1)) { MessageService.ShowCustomDialog (new RenameItemDialog (options, this)); return; } var par = options.SelectedItem as IParameter; if (par != null && par.Owner != null && (par.Owner.Accessibility != Accessibility.Private || par.Owner.DeclaringTypeDefinition != null && par.Owner.DeclaringTypeDefinition.Parts.Count > 1)) { MessageService.ShowCustomDialog (new RenameItemDialog (options, this)); return; } var col = ReferenceFinder.FindReferences (options.SelectedItem, true); if (col == null) return; var data = options.Document != null ? options.GetTextEditorData () : IdeApp.Workbench.ActiveDocument.Editor; var editor = data.Parent; if (editor == null) { MessageService.ShowCustomDialog (new RenameItemDialog (options, this)); return; } var links = new List (); var link = new TextLink ("name"); int baseOffset = Int32.MaxValue; foreach (var r in col) { baseOffset = Math.Min (baseOffset, r.Offset); } foreach (MemberReference r in col) { var segment = new TextSegment (r.Offset - baseOffset, r.Length); if (segment.Offset <= data.Caret.Offset - baseOffset && data.Caret.Offset - baseOffset <= segment.EndOffset) { link.Links.Insert (0, segment); } else { link.AddLink (segment); } } links.Add (link); if (editor.CurrentMode is TextLinkEditMode) ((TextLinkEditMode)editor.CurrentMode).ExitTextLinkMode (); TextLinkEditMode tle = new TextLinkEditMode (editor, baseOffset, links); tle.SetCaretPosition = false; tle.SelectPrimaryLink = true; if (tle.ShouldStartTextLinkMode) { tle.Cancel += delegate { if (tle.HasChangedText) editor.Document.Undo (); }; tle.OldMode = data.CurrentMode; tle.StartMode (); data.CurrentMode = tle; } } else { MessageService.ShowCustomDialog (new RenameItemDialog (options, this)); } } public class RenameProperties { public string NewName { get; set; } public bool RenameFile { get; set; } public bool IncludeOverloads { get; set; } } public override List PerformChanges (RefactoringOptions options, object prop) { RenameProperties properties = (RenameProperties)prop; List result = new List (); IEnumerable col = null; using (var monitor = new MessageDialogProgressMonitor (true, false, false, true)) { col = ReferenceFinder.FindReferences (options.SelectedItem, properties.IncludeOverloads, monitor); if (col == null) return result; if (properties.RenameFile && options.SelectedItem is IType) { var cls = ((IType)options.SelectedItem).GetDefinition (); int currentPart = 1; var alreadyRenamed = new HashSet (); foreach (var part in cls.Parts) { if (alreadyRenamed.Contains (part.Region.FileName)) continue; alreadyRenamed.Add (part.Region.FileName); string oldFileName = System.IO.Path.GetFileNameWithoutExtension (part.Region.FileName); string newFileName; var newName = properties.NewName; if (string.IsNullOrEmpty (oldFileName) || string.IsNullOrEmpty (newName)) continue; if (oldFileName.ToUpper () == newName.ToUpper () || oldFileName.ToUpper ().EndsWith ("." + newName.ToUpper (), StringComparison.Ordinal)) continue; int idx = oldFileName.IndexOf (cls.Name, StringComparison.Ordinal); if (idx >= 0) { newFileName = oldFileName.Substring (0, idx) + newName + oldFileName.Substring (idx + cls.Name.Length); } else { newFileName = currentPart != 1 ? newName + currentPart : newName; currentPart++; } int t = 0; while (System.IO.File.Exists (GetFullFileName (newFileName, part.Region.FileName, t))) { t++; } result.Add (new RenameFileChange (part.Region.FileName, GetFullFileName (newFileName, part.Region.FileName, t))); } } foreach (var memberRef in col) { TextReplaceChange change = new TextReplaceChange (); change.FileName = memberRef.FileName; change.Offset = memberRef.Offset; change.RemovedChars = memberRef.Length; change.InsertedText = properties.NewName; change.Description = string.Format (GettextCatalog.GetString ("Replace '{0}' with '{1}'"), memberRef.GetName (), properties.NewName); result.Add (change); } } return result; } static string GetFullFileName (string fileName, string oldFullFileName, int tryCount) { StringBuilder name = new StringBuilder (fileName); if (tryCount > 0) { name.Append ("_"); name.Append (tryCount.ToString ()); } if (System.IO.Path.HasExtension (oldFullFileName)) name.Append (System.IO.Path.GetExtension (oldFullFileName)); return System.IO.Path.Combine (System.IO.Path.GetDirectoryName (oldFullFileName), name.ToString ()); } } }