From f18f9a68174a47707cb598845c5058acbd2eee8a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 1 Aug 2008 21:59:10 +0000 Subject: [PATCH] Add "Add check for null" and "Add range check" commands to C# refactoring menu. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3279 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../CSharpBinding/Project/CSharpBinding.addin | 4 + .../Project/CSharpBinding.csproj | 1 + .../ParameterCheckRefactoringMenuBuilder.cs | 113 ++++++++++++++++++ .../CSharpDesignerGenerator.cs | 1 - .../Project/Src/Gui/Caret.cs | 2 +- .../RefactoringMenuBuilder.cs | 26 +++- 6 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/ParameterCheckRefactoringMenuBuilder.cs diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin index ab7d54bcde..8d7701c3e9 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin @@ -110,6 +110,10 @@ projectfileextension = ".csproj" class = "CSharpBinding.CSharpLanguageBinding" /> + + + + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index 6f752dbb47..4cfe710cf8 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -56,6 +56,7 @@ Form + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/ParameterCheckRefactoringMenuBuilder.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/ParameterCheckRefactoringMenuBuilder.cs new file mode 100644 index 0000000000..b579074ffc --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/ParameterCheckRefactoringMenuBuilder.cs @@ -0,0 +1,113 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Windows.Forms; + +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; +using ICSharpCode.SharpDevelop.Dom.Refactoring; +using ICSharpCode.SharpDevelop.Refactoring; +using ICSharpCode.TextEditor; +using ICSharpCode.TextEditor.Actions; +using ICSharpCode.TextEditor.Document; + +namespace CSharpBinding +{ + /// + /// Description of ParameterCheckRefactorings. + /// + public class ParameterCheckRefactoringMenuBuilder : ISubmenuBuilder + { + public ToolStripItem[] BuildSubmenu(Codon codon, object owner) + { + List resultItems = new List(); + RefactoringMenuContext context = (RefactoringMenuContext)owner; + LocalResolveResult lrr = context.ResolveResult as LocalResolveResult; + if (lrr == null || lrr.CallingClass == null || lrr.ResolvedType == null) + return resultItems.ToArray(); + LanguageProperties language = lrr.CallingClass.ProjectContent.Language; + if (language != LanguageProperties.CSharp) + return resultItems.ToArray(); + + IClass parameterTypeClass = lrr.ResolvedType.GetUnderlyingClass(); + if (parameterTypeClass == null || parameterTypeClass.ClassType != ClassType.Enum && parameterTypeClass.ClassType != ClassType.Struct) { + // the parameter is a reference type + resultItems.Add(MakeItem("Add check for null", delegate { AddCheckForNull(context); })); + } + if (parameterTypeClass != null) { + if (parameterTypeClass.FullyQualifiedName == "System.Int32") { + resultItems.Add(MakeItem("Add range check", delegate { AddRangeCheck(context); })); + } + } + return resultItems.ToArray(); + } + + ToolStripMenuItem MakeItem(string title, EventHandler onClick) + { + ToolStripMenuItem menuItem = new ToolStripMenuItem(StringParser.Parse(title)); + menuItem.Click += onClick; + return menuItem; + } + + void AddCheck(RefactoringMenuContext context, string newCode) + { + var codeGen = context.ResolveResult.CallingClass.ProjectContent.Language.CodeGenerator; + TextArea textArea = context.TextArea; + IMember m = context.ResolveResult.CallingMember; + TextLocation methodStart = FindMethodStart(textArea.Document, m.BodyRegion); + if (methodStart.IsEmpty) + return; + textArea.Caret.Position = methodStart; + textArea.SelectionManager.ClearSelection(); + textArea.Document.UndoStack.StartUndoGroup(); + try { + foreach (string newCodeLine in newCode.Split('\n')) { + new Return().Execute(textArea); + textArea.InsertString(newCodeLine); + } + } finally { + textArea.Document.UndoStack.EndUndoGroup(); + } + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + textArea.Document.CommitUpdate(); + } + + void AddCheckForNull(RefactoringMenuContext context) + { + string name = ((LocalResolveResult)context.ResolveResult).VariableName; + AddCheck(context, + "if (" + name + " == null)\n" + + "throw new ArgumentNullException(\"" + name + "\");"); + } + + void AddRangeCheck(RefactoringMenuContext context) + { + string name = ((LocalResolveResult)context.ResolveResult).VariableName; + AddCheck(context, + "if (" + name + " < 0 || " + name + " > upper_bound)\n" + + "throw new ArgumentOutOfRangeException(\"" + name + "\", " + name + ", \"Value must be between 0 and \" + upper_bound);"); + } + + static TextLocation FindMethodStart(ICSharpCode.TextEditor.Document.IDocument document, DomRegion bodyRegion) + { + if (bodyRegion.IsEmpty) + return TextLocation.Empty; + int offset = document.PositionToOffset(new TextLocation(bodyRegion.BeginColumn - 1, bodyRegion.BeginLine - 1)); + while (offset < document.TextLength) { + if (document.GetCharAt(offset) == '{') { + return document.OffsetToPosition(offset + 1); + } + offset++; + } + return TextLocation.Empty; + } + } +} diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/CSharpDesignerGenerator.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/CSharpDesignerGenerator.cs index 32cb4a49c1..677fdbcc7c 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/CSharpDesignerGenerator.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/CSharpDesignerGenerator.cs @@ -64,7 +64,6 @@ namespace ICSharpCode.FormsDesigner DomRegion r = method.BodyRegion; int offset = document.PositionToOffset(new TextLocation(r.BeginColumn - 1, r.BeginLine - 1)); - string tmp = document.GetText(offset, 10); while (offset < document.TextLength) { char c = document.GetCharAt(offset++); if (c == '{') { diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/Caret.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/Caret.cs index b481ae28d2..2e852a26c8 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/Caret.cs +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/Caret.cs @@ -294,7 +294,7 @@ namespace ICSharpCode.TextEditor } #region Caret implementation - public void PaintCaret(Graphics g) + internal void PaintCaret(Graphics g) { caretImplementation.PaintCaret(g); } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs index 33cd35e03b..e21438a3ec 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Windows.Forms; @@ -21,6 +22,14 @@ using ICSharpCode.TextEditor.Document; namespace ICSharpCode.SharpDevelop.Refactoring { + public class RefactoringMenuContext + { + public TextArea TextArea; + public ExpressionResult ExpressionResult; + public ResolveResult ResolveResult; + public bool IsDefinition; + } + /// /// Build a menu with refactoring commands for the item that has been clicked on in the text editor. /// @@ -80,6 +89,11 @@ namespace ICSharpCode.SharpDevelop.Refactoring expressionResult = FindFullExpressionAtCaret(textArea, expressionFinder); repeatResolve: rr = ResolveExpressionAtCaret(textArea, expressionResult); + RefactoringMenuContext context = new RefactoringMenuContext { + TextArea = textArea, + ResolveResult = rr, + ExpressionResult = expressionResult + }; item = null; if (rr is MethodGroupResolveResult) { item = MakeItem(definitions, ((MethodGroupResolveResult)rr).GetMethodIfSingleOverload()); @@ -97,7 +111,8 @@ namespace ICSharpCode.SharpDevelop.Refactoring } else if (rr is TypeResolveResult) { item = MakeItem(definitions, ((TypeResolveResult)rr).ResolvedClass); } else if (rr is LocalResolveResult) { - item = MakeItem((LocalResolveResult)rr, caretLine + 1 == ((LocalResolveResult)rr).VariableDefinitionRegion.BeginLine); + context.IsDefinition = caretLine + 1 == ((LocalResolveResult)rr).VariableDefinitionRegion.BeginLine; + item = MakeItem((LocalResolveResult)rr, context); insertIndex = 0; // Insert local variable menu item at the topmost position. } else if (rr is UnknownIdentifierResolveResult) { item = MakeItemForResolveError((UnknownIdentifierResolveResult)rr, expressionResult.Context, textArea); @@ -205,16 +220,17 @@ namespace ICSharpCode.SharpDevelop.Refactoring return null; } - ToolStripMenuItem MakeItem(LocalResolveResult local, bool isDefinition) + ToolStripMenuItem MakeItem(LocalResolveResult local, RefactoringMenuContext context) { + Debug.Assert(local == context.ResolveResult); ToolStripMenuItem item = MakeItemInternal(local.VariableName, local.IsParameter ? ClassBrowserIconService.ParameterIndex : ClassBrowserIconService.LocalVariableIndex, local.CallingClass.CompilationUnit, - isDefinition ? DomRegion.Empty : local.VariableDefinitionRegion); + context.IsDefinition ? DomRegion.Empty : local.VariableDefinitionRegion); string treePath = "/SharpDevelop/ViewContent/DefaultTextEditor/Refactoring/"; treePath += local.IsParameter ? "Parameter" : "LocalVariable"; - if (isDefinition) treePath += "Definition"; - MenuService.AddItemsToMenu(item.DropDown.Items, local, treePath); + if (context.IsDefinition) treePath += "Definition"; + MenuService.AddItemsToMenu(item.DropDown.Items, context, treePath); return item; }