Browse Source

Improved "ExtractMethod" refactoring

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3330 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Siegfried Pammer 17 years ago
parent
commit
f5e2708407
  1. 38
      src/AddIns/Misc/SharpRefactoring/SharpRefactoring.addin
  2. 6
      src/AddIns/Misc/SharpRefactoring/SharpRefactoring.csproj
  3. 171
      src/AddIns/Misc/SharpRefactoring/Src/CSharpMethodExtractor.cs
  4. 411
      src/AddIns/Misc/SharpRefactoring/Src/ExtractMethod.cs
  5. 64
      src/AddIns/Misc/SharpRefactoring/Src/ExtractMethodCommand.cs
  6. 4
      src/AddIns/Misc/SharpRefactoring/Src/Forms/ExtractMethodForm.cs
  7. 302
      src/AddIns/Misc/SharpRefactoring/Src/MethodExtractorBase.cs
  8. 65
      src/AddIns/Misc/SharpRefactoring/Src/Transformers/ReplaceUnnecessaryVariableDeclarationsTransformer.cs
  9. 31
      src/AddIns/Misc/SharpRefactoring/Src/Visitors/FindReferenceVisitor.cs

38
src/AddIns/Misc/SharpRefactoring/SharpRefactoring.addin

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
<AddIn name = "SharpRefactoring"
author = "Siegfried Pammer"
description = "Provides refactorings for C# in SharpDevelop">
description = "Provides refactorings in SharpDevelop">
<Manifest>
<Identity name="ICSharpCode.SharpRefactoring"/>
@ -14,43 +14,9 @@ @@ -14,43 +14,9 @@
<MenuItem id="parameterCheckRefactorings" type="Builder" class="SharpRefactoring.ParameterCheckRefactoringMenuBuilder"/>
</Path>
<Path name = "/SharpDevelop/ViewContent/DefaultTextEditor/ContextMenu">
<MenuItem id = "SharpRefactoring" label = "Refactoring" type="Menu">
<MenuItem id = "ExtractMethod"
label = "Extract method"
class = "SharpRefactoring.ExtractMethod"/>
<!-- <MenuItem id = "RemoveUnusedParams"
label = "Remove unused parameters"
class = "SharpRefactoring.RemoveUnusedParameters"/> -->
</MenuItem>
</Path>
<Path name = "/SharpDevelop/Workbench/MainMenu/Refactor">
<MenuItem id = "ExtractMethod"
label = "Extract method"
shortcut = "Alt|R"
class = "SharpRefactoring.ExtractMethod"/>
<!-- <MenuItem id = "Separator1" type = "Separator"/>
<MenuItem id = "ConvertPropertyToMethods"
label = "Convert property to method(s)"
class = "SharpRefactoring.ConvertPropertyToMethods"/>
<MenuItem id = "SafeDelete"
label = "Safe Delete"
class = "SharpRefactoring.SafeDelete"/>-->
class = "SharpRefactoring.ExtractMethodCommand"/>
</Path>
<!-- <Path name = "/SharpDevelop/Pads/ErrorList/TaskContextMenu">
<MenuItem id = "QuickFixes"
label = "QuickFixes"
class = "SharpRefactoring.QuickFixes.QuickFix"/>
</Path>-->
<!-- <Path name = "/SharpDevelop/Workbench/Pads">
<Pad id = "PreviewPanel"
category = "Main"
title = "Refactoring Preview"
icon = ""
shortcut = ""
class = "SharpRefactoring.PreviewPanel"/>
</Path>-->
</AddIn>

6
src/AddIns/Misc/SharpRefactoring/SharpRefactoring.csproj

@ -56,14 +56,17 @@ @@ -56,14 +56,17 @@
<Link>Configuration\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Configuration\AssemblyInfo.cs" />
<Compile Include="Src\ExtractMethod.cs" />
<Compile Include="Src\CSharpMethodExtractor.cs" />
<Compile Include="Src\ExtractMethodCommand.cs" />
<Compile Include="Src\Forms\ExtractMethodForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Src\Forms\ExtractMethodForm.Designer.cs">
<DependentUpon>ExtractMethodForm.cs</DependentUpon>
</Compile>
<Compile Include="Src\MethodExtractorBase.cs" />
<Compile Include="Src\ParameterCheckRefactoringMenuBuilder.cs" />
<Compile Include="Src\Transformers\ReplaceUnnecessaryVariableDeclarationsTransformer.cs" />
<Compile Include="Src\Visitors\FindJumpInstructionsVisitor.cs" />
<Compile Include="Src\Visitors\FindLocalVariablesVisitor.cs" />
<Compile Include="Src\Visitors\FindMemberVisitor.cs" />
@ -105,5 +108,6 @@ @@ -105,5 +108,6 @@
<Name>ICSharpCode.SharpDevelop.Dom</Name>
<Private>False</Private>
</ProjectReference>
<Folder Include="Src\Transformers" />
</ItemGroup>
</Project>

171
src/AddIns/Misc/SharpRefactoring/Src/CSharpMethodExtractor.cs

@ -0,0 +1,171 @@ @@ -0,0 +1,171 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="sie_pam@gmx.at"/>
// <version>$Revision: 3287 $</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.PrettyPrinter;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.TextEditor.Document;
using SharpRefactoring.Transformers;
using SharpRefactoring.Visitors;
namespace SharpRefactoring
{
public class CSharpMethodExtractor : MethodExtractorBase
{
public CSharpMethodExtractor(ICSharpCode.TextEditor.TextEditorControl textEditor, ISelection selection)
: base(textEditor, selection, new CSharpOutputVisitor())
{
}
protected override string GenerateCode(INode unit, bool installSpecials)
{
CSharpOutputVisitor visitor = new CSharpOutputVisitor();
if (installSpecials) {
SpecialNodesInserter.Install(this.specialsList, visitor);
}
unit.AcceptVisitor(visitor, null);
return visitor.Text;
}
public override bool Extract()
{
using (IParser parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader("class Tmp { void Test() {\n " + this.currentSelection.SelectedText + "\n}}"))) {
parser.Parse();
if (parser.Errors.Count > 0) {
MessageService.ShowError("Errors occured during parsing! Cannot extract a new method!");
return false;
}
this.specialsList = parser.Lexer.SpecialTracker.RetrieveSpecials();
}
MethodDeclaration newMethod = new MethodDeclaration();
List<VariableDeclaration> possibleReturnValues = new List<VariableDeclaration>();
List<VariableDeclaration> otherReturnValues = new List<VariableDeclaration>();
// Initialise new method
newMethod.Body = GetBlock(this.currentSelection.SelectedText);
newMethod.Body.StartLocation = new Location(0,0);
this.parentNode = GetParentMember(this.currentSelection.StartPosition.Line, this.currentSelection.StartPosition.Column, this.currentSelection.EndPosition.Line, this.currentSelection.EndPosition.Column);
if (parentNode == null) {
MessageService.ShowError("Invalid selection! Please select a valid range.");
return false;
}
if (!CheckForJumpInstructions(newMethod, this.currentSelection))
return false;
newMethod.Modifier = parentNode.Modifier;
newMethod.Modifier &= ~(Modifiers.Internal | Modifiers.Protected | Modifiers.Private | Modifiers.Public);
foreach (ParameterDeclarationExpression pde in parentNode.Parameters)
{
FindReferenceVisitor frv = new FindReferenceVisitor(true, pde.ParameterName, newMethod.Body.StartLocation, newMethod.Body.EndLocation);
newMethod.AcceptVisitor(frv, null);
if (frv.Identifiers.Count > 0) {
bool isIn = true;
foreach (IdentifierExpression identifier in frv.Identifiers) {
if (!IsInSel(identifier.StartLocation, this.currentSelection))
isIn = false;
}
if (isIn) {
possibleReturnValues.Add(new VariableDeclaration(pde.ParameterName, null, pde.TypeReference));
}
bool hasOccurrences = HasOccurrencesAfter(true, parentNode, new Location(this.currentSelection.EndPosition.Column + 1, this.currentSelection.EndPosition.Line + 1), pde.ParameterName, newMethod.Body.StartLocation, newMethod.Body.EndLocation);
if (hasOccurrences)
newMethod.Parameters.Add(new ParameterDeclarationExpression(pde.TypeReference, pde.ParameterName, ParameterModifiers.Ref));
else
newMethod.Parameters.Add(new ParameterDeclarationExpression(pde.TypeReference, pde.ParameterName, pde.ParamModifier));
}
}
LookupTableVisitor ltv = new LookupTableVisitor(SupportedLanguage.CSharp);
parentNode.AcceptVisitor(ltv, null);
foreach (KeyValuePair<string, List<LocalLookupVariable>> pair in ltv.Variables) {
foreach (LocalLookupVariable variable in pair.Value) {
if (IsInSel(variable.StartPos, this.currentSelection) && HasOccurrencesAfter(true, this.parentNode, new Location(this.currentSelection.EndPosition.Column + 1, this.currentSelection.EndPosition.Line + 1), variable.Name, variable.StartPos, variable.EndPos)) {
possibleReturnValues.Add(new VariableDeclaration(variable.Name, variable.Initializer, variable.TypeRef));
otherReturnValues.Add(new VariableDeclaration(variable.Name, variable.Initializer, variable.TypeRef));
}
FindReferenceVisitor frv = new FindReferenceVisitor(true, variable.Name, variable.StartPos, variable.EndPos);
parentNode.AcceptVisitor(frv, null);
if ((frv.Identifiers.Count > 0) && (!(IsInSel(variable.StartPos, this.currentSelection) || IsInSel(variable.EndPos, this.currentSelection)))) {
bool hasOccurrences = HasOccurrencesAfter(true, this.parentNode, new Location(this.currentSelection.EndPosition.Column + 1, this.currentSelection.EndPosition.Line + 1), variable.Name, variable.StartPos, variable.EndPos);
bool isInitialized = IsInitializedVariable(true, this.parentNode, variable);
bool hasAssignment = HasAssignment(newMethod, variable);
bool getsAssigned = pair.Value.Count > 0;
if (hasOccurrences && isInitialized)
newMethod.Parameters.Add(new ParameterDeclarationExpression(variable.TypeRef, variable.Name, ParameterModifiers.Ref));
else {
if (hasOccurrences && hasAssignment)
newMethod.Parameters.Add(new ParameterDeclarationExpression(variable.TypeRef, variable.Name, ParameterModifiers.Out));
else {
if (!hasOccurrences && getsAssigned)
newMethod.Parameters.Add(new ParameterDeclarationExpression(variable.TypeRef, variable.Name, ParameterModifiers.None));
else {
if (!hasOccurrences && !isInitialized)
newMethod.Body.Children.Insert(0, new LocalVariableDeclaration(new VariableDeclaration(variable.Name, variable.Initializer, variable.TypeRef)));
else
newMethod.Parameters.Add(new ParameterDeclarationExpression(variable.TypeRef, variable.Name, ParameterModifiers.In));
}
}
}
}
}
}
List<VariableDeclaration> paramsAsVarDecls = new List<VariableDeclaration>();
this.beforeCallDeclarations = new List<LocalVariableDeclaration>();
for (int i = 0; i < otherReturnValues.Count - 1; i++) {
VariableDeclaration varDecl = otherReturnValues[i];
paramsAsVarDecls.Add(varDecl);
ParameterDeclarationExpression p = new ParameterDeclarationExpression(varDecl.TypeReference, varDecl.Name);
p.ParamModifier = ParameterModifiers.Out;
if (!newMethod.Parameters.Contains(p)) {
newMethod.Parameters.Add(p);
} else {
this.beforeCallDeclarations.Add(new LocalVariableDeclaration(varDecl));
}
}
ReplaceUnnecessaryVariableDeclarationsTransformer t = new ReplaceUnnecessaryVariableDeclarationsTransformer(paramsAsVarDecls);
newMethod.AcceptVisitor(t, null);
CreateReturnStatement(newMethod, possibleReturnValues);
this.extractedMethod = newMethod;
return true;
}
}
}

411
src/AddIns/Misc/SharpRefactoring/Src/ExtractMethod.cs

@ -1,411 +0,0 @@ @@ -1,411 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.PrettyPrinter;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Refactoring;
using ICSharpCode.TextEditor.Document;
using SharpRefactoring.Visitors;
using Dom = ICSharpCode.SharpDevelop.Dom;
using SharpRefactoring.Forms;
using System.Text.RegularExpressions;
namespace SharpRefactoring
{
public class ExtractMethod : AbstractRefactoringCommand
{
ISelection currentSelection;
IDocument currentDocument;
ICSharpCode.SharpDevelop.Dom.Refactoring.CodeGenerator generator;
ParametrizedNode parent;
VariableDeclaration returnVar;
IList<ISpecial> specialsList;
protected override void Run(ICSharpCode.TextEditor.TextEditorControl textEditor, ICSharpCode.SharpDevelop.Dom.Refactoring.RefactoringProvider provider)
{
if (textEditor.ActiveTextAreaControl.SelectionManager.HasSomethingSelected)
{
this.currentDocument = textEditor.Document;
this.currentSelection = textEditor.ActiveTextAreaControl.SelectionManager.SelectionCollection[0];
this.generator = ProjectService.CurrentProject.LanguageProperties.CodeGenerator;
MethodDeclaration method = GetMethod(this.currentDocument, this.currentSelection);
if (method == null) return;
Statement caller;
InvocationExpression expr = new InvocationExpression(new IdentifierExpression(method.Name), CreateArgumentExpressions(method.Parameters));
if (method.TypeReference.Type != "void") {
if (parent is MethodDeclaration) {
if (method.TypeReference == (parent as MethodDeclaration).TypeReference)
caller = new ReturnStatement(expr);
else {
this.returnVar.Initializer = expr;
caller = new LocalVariableDeclaration(this.returnVar);
}
} else {
this.returnVar.Initializer = expr;
caller = new LocalVariableDeclaration(this.returnVar);
}
} else {
caller = new ExpressionStatement(expr);
}
TextEditorDocument doc = new TextEditorDocument(this.currentDocument);
string line = this.currentDocument.GetText(this.currentDocument.GetLineSegment(this.currentSelection.StartPosition.Line));
string indent = "";
foreach (char c in line) {
if ((c == ' ') || (c == '\t'))
indent += c;
else
break;
}
textEditor.Document.UndoStack.StartUndoGroup();
// TODO : Implement VB.NET support
IOutputAstVisitor csOutput = new CSharpOutputVisitor();
// FIXME : Problems with comments at the begin of the selection
RemoveUnneededSpecials();
using (SpecialNodesInserter.Install(this.specialsList, csOutput)) {
method.AcceptVisitor(csOutput, null);
string code = "\n\n" + csOutput.Text;
code = code.TrimEnd('\n', ' ', '\t');
Dom.IMember p = GetParentMember(textEditor, this.currentSelection.StartPosition.Line, this.currentSelection.StartPosition.Column);
textEditor.Document.Insert(textEditor.Document.PositionToOffset(
new ICSharpCode.TextEditor.TextLocation(
p.BodyRegion.EndColumn - 1, p.BodyRegion.EndLine - 1)
), code);
}
string call = indent + GenerateCode(new CSharpOutputVisitor(), caller);
call += (textEditor.ActiveTextAreaControl.SelectionManager.SelectedText.EndsWith("\n")) ? "\n" : "";
textEditor.Document.Replace(currentSelection.Offset, currentSelection.Length, call);
textEditor.Document.FormattingStrategy.IndentLines(textEditor.ActiveTextAreaControl.TextArea, 0, textEditor.Document.TotalNumberOfLines - 1);
textEditor.Document.UndoStack.EndUndoGroup();
textEditor.ActiveTextAreaControl.SelectionManager.ClearSelection();
}
}
void RemoveUnneededSpecials()
{
int i = 0;
while (i < this.specialsList.Count) {
ISpecial spec = this.specialsList[i];
if (!IsInSel(spec.EndPosition, this.currentSelection) || !IsInSel(spec.StartPosition, this.currentSelection)) {
this.specialsList.RemoveAt(i);
continue;
} else {
if (spec is Comment) {
Comment comment = spec as Comment;
this.specialsList[i] = new Comment(comment.CommentType, comment.CommentText,
new Location(spec.StartPosition.Column, spec.StartPosition.Line - this.currentSelection.StartPosition.Line + 1),
new Location(spec.EndPosition.Column, spec.EndPosition.Line - this.currentSelection.StartPosition.Line + 1));
} else {
if (spec is PreprocessingDirective) {
PreprocessingDirective ppd = spec as PreprocessingDirective;
this.specialsList[i] = new PreprocessingDirective(ppd.Cmd, ppd.Arg,
new Location(spec.StartPosition.Column, spec.StartPosition.Line - this.currentSelection.StartPosition.Line + 1),
new Location(spec.EndPosition.Column, spec.EndPosition.Line - this.currentSelection.StartPosition.Line + 1));
} else {
this.specialsList[i] = new BlankLine(new Location(spec.StartPosition.Column, spec.StartPosition.Line - this.currentSelection.StartPosition.Line + 1));
}
}
}
i++;
}
}
static List<Expression> CreateArgumentExpressions(List<ParameterDeclarationExpression> parameters)
{
List<Expression> expressions = new List<Expression>();
foreach (ParameterDeclarationExpression pde in parameters)
{
expressions.Add(new DirectionExpression(
(FieldDirection)Enum.Parse(typeof(FieldDirection),pde.ParamModifier.ToString()),
new IdentifierExpression(pde.ParameterName)));
}
return expressions;
}
CompilationUnit GetCurrentCompilationUnit(IDocument doc)
{
using (IParser parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(doc.TextContent))) {
parser.Parse();
this.specialsList = parser.Lexer.SpecialTracker.RetrieveSpecials();
if (parser.Errors.Count > 0) {
MessageService.ShowError(null, parser.Errors.ErrorOutput);
return null;
}
return parser.CompilationUnit;
}
}
MethodDeclaration GetMethod(ICSharpCode.TextEditor.Document.IDocument doc, ISelection sel)
{
MethodDeclaration newMethod = new MethodDeclaration();
CompilationUnit unit = GetCurrentCompilationUnit(doc);
if (unit == null)
return null;
// Initialise new method
newMethod.Body = GetBlock(sel.SelectedText);
newMethod.Body.StartLocation = new Location(0,0);
this.parent = GetParentMember(unit, sel.StartPosition.Line, sel.StartPosition.Column, sel.EndPosition.Line, sel.EndPosition.Column);
if (parent == null) {
MessageService.ShowError("Invalid selection! Please select a valid range.");
return null;
}
if (!CheckForJumpInstructions(newMethod, sel))
return null;
newMethod.Modifier = parent.Modifier;
newMethod.Modifier &= ~(Modifiers.Internal | Modifiers.Protected | Modifiers.Private | Modifiers.Public);
List<VariableDeclaration> possibleReturnValues = new List<VariableDeclaration>();
foreach (ParameterDeclarationExpression pde in parent.Parameters)
{
FindReferenceVisitor frv = new FindReferenceVisitor(pde.ParameterName, pde.TypeReference);
newMethod.AcceptVisitor(frv, null);
if (frv.Identifiers.Count > 0) {
bool isIn = true;
foreach (IdentifierExpression identifier in frv.Identifiers) {
if (!IsInSel(identifier.StartLocation, sel))
isIn = false;
}
if (isIn) {
possibleReturnValues.Add(new VariableDeclaration(pde.ParameterName, null, pde.TypeReference));
}
bool hasOccurrences = HasOccurrencesAfter(parent, new Location(sel.EndPosition.Column + 1, sel.EndPosition.Line + 1), pde.ParameterName, pde.TypeReference); if (hasOccurrences)
newMethod.Parameters.Add(new ParameterDeclarationExpression(pde.TypeReference, pde.ParameterName, ParameterModifiers.Ref));
else
newMethod.Parameters.Add(new ParameterDeclarationExpression(pde.TypeReference, pde.ParameterName, ParameterModifiers.In));
}
}
FindLocalVariablesVisitor flvv = new FindLocalVariablesVisitor();
parent.AcceptVisitor(flvv, null);
foreach (VariableDeclaration lvd in flvv.Variables)
{
FindReferenceVisitor frv = new FindReferenceVisitor(lvd.Name, lvd.TypeReference);
newMethod.AcceptVisitor(frv, null);
if (IsInSel(lvd.StartLocation, sel) && HasOccurrencesAfter(parent, new Location(sel.EndPosition.Column + 1, sel.EndPosition.Line + 1), lvd.Name, lvd.TypeReference))
possibleReturnValues.Add(new VariableDeclaration(lvd.Name, null, lvd.TypeReference));
if ((frv.Identifiers.Count > 0) && (!(IsInSel(lvd.StartLocation, sel) || IsInSel(lvd.EndLocation, sel)))) {
bool hasOccurrences = HasOccurrencesAfter(parent, new Location(sel.EndPosition.Column + 1, sel.EndPosition.Line + 1), lvd.Name, lvd.TypeReference);
bool isInitialized = IsInitializedVariable(parent, lvd);
bool hasAssignment = HasAssignment(newMethod, lvd);
if (hasOccurrences && isInitialized)
newMethod.Parameters.Add(new ParameterDeclarationExpression(lvd.TypeReference, lvd.Name, ParameterModifiers.Ref));
else {
if (hasOccurrences && hasAssignment)
newMethod.Parameters.Add(new ParameterDeclarationExpression(lvd.TypeReference, lvd.Name, ParameterModifiers.Out));
else {
if (!hasOccurrences && !isInitialized)
newMethod.Body.Children.Insert(0, new LocalVariableDeclaration(lvd));
else
newMethod.Parameters.Add(new ParameterDeclarationExpression(lvd.TypeReference, lvd.Name, ParameterModifiers.In));
}
}
}
}
HasReturnStatementVisitor hrsv = new HasReturnStatementVisitor();
newMethod.AcceptVisitor(hrsv, null);
if (hrsv.HasReturn) {
if (parent is MethodDeclaration)
newMethod.TypeReference = (parent as MethodDeclaration).TypeReference;
if (parent is PropertyDeclaration)
newMethod.TypeReference = (parent as PropertyDeclaration).TypeReference;
if (parent is OperatorDeclaration)
newMethod.TypeReference = (parent as OperatorDeclaration).TypeReference;
} else {
if (possibleReturnValues.Count > 0) {
newMethod.TypeReference = possibleReturnValues[0].TypeReference;
newMethod.Body.Children.Add(new ReturnStatement(new IdentifierExpression(possibleReturnValues[0].Name)));
this.returnVar = possibleReturnValues[0];
} else
newMethod.TypeReference = new TypeReference("void");
}
IOutputAstVisitor output = new CSharpOutputVisitor();
newMethod.Name = "NewMethod";
BlockStatement body = newMethod.Body;
newMethod.Body = new BlockStatement();
newMethod.AcceptVisitor(output, null);
string preview = output.Text;
ExtractMethodForm form = new ExtractMethodForm("NewMethod", preview);
if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
newMethod.Name = form.Text;
newMethod.Body = body;
}
else
{
return null;
}
return newMethod;
}
Dom.IMember GetParentMember(ICSharpCode.TextEditor.TextEditorControl textEditor, int line, int column)
{
Dom.ParseInformation parseInfo = ParserService.GetParseInformation(textEditor.FileName);
if (parseInfo != null) {
Dom.IClass c = parseInfo.MostRecentCompilationUnit.GetInnermostClass(line, column);
if (c != null) {
foreach (Dom.IMember member in c.Properties) {
if (member.BodyRegion.IsInside(line, column)) {
return member;
}
}
foreach (Dom.IMember member in c.Methods) {
if (member.BodyRegion.IsInside(line, column)) {
return member;
}
}
}
}
return null;
}
static bool CheckForJumpInstructions(MethodDeclaration method, ISelection selection)
{
FindJumpInstructionsVisitor fjiv = new FindJumpInstructionsVisitor(method, selection);
method.AcceptVisitor(fjiv, null);
return fjiv.IsOk;
}
static bool IsInSel(Location location, ISelection sel)
{
bool result = (sel.ContainsPosition(new ICSharpCode.TextEditor.TextLocation(location.Column - 1, location.Line - 1)));
return result;
}
static BlockStatement GetBlock(string data)
{
data = "class Temp { public void t() {" + data + "} }";
using (IParser parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(data))) {
parser.Parse();
if (parser.Errors.Count > 0) {
throw new ArgumentException("Invalid selection! Please select a valid range!");
}
MethodDeclaration method = (MethodDeclaration)(parser.CompilationUnit.Children[0].Children[0]);
return method.Body;
}
}
static string GenerateCode(IOutputAstVisitor outputVisitor, INode unit)
{
unit.AcceptVisitor(outputVisitor, null);
return outputVisitor.Text;
}
static ParametrizedNode GetParentMember(CompilationUnit unit, int startLine, int startColumn, int endLine, int endColumn)
{
FindMemberVisitor fmv = new FindMemberVisitor(startColumn, startLine, endColumn, endLine);
unit.AcceptVisitor(fmv, null);
return fmv.Member;
}
static bool HasOccurrencesAfter(ParametrizedNode member, Location location, string name, TypeReference type)
{
FindReferenceVisitor frv = new FindReferenceVisitor(name, type);
member.AcceptVisitor(frv, null);
foreach (IdentifierExpression identifier in frv.Identifiers)
{
if (identifier.StartLocation > location)
return true;
}
return false;
}
bool IsInitializedVariable(ParametrizedNode member, VariableDeclaration variable)
{
if (!(variable.Initializer.IsNull)) {
return true;
} else {
FindReferenceVisitor frv = new FindReferenceVisitor(variable.Name, variable.TypeReference);
member.AcceptVisitor(frv, null);
foreach (IdentifierExpression expr in frv.Identifiers) {
if ((expr.EndLocation < new Location(currentSelection.StartPosition.Column, currentSelection.StartPosition.Line)) &&
!(expr.IsNull))
return true;
}
}
return false;
}
static bool HasAssignment(MethodDeclaration method, VariableDeclaration variable)
{
HasAssignmentsVisitor hav = new HasAssignmentsVisitor(variable.Name, variable.TypeReference);
method.AcceptVisitor(hav, null);
return hav.HasAssignment;
}
}
}

64
src/AddIns/Misc/SharpRefactoring/Src/ExtractMethodCommand.cs

@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="sie_pam@gmx.at"/>
// <version>$Revision$</version>
// </file>
using ICSharpCode.TextEditor;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.PrettyPrinter;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Refactoring;
using ICSharpCode.TextEditor.Document;
using SharpRefactoring.Forms;
using SharpRefactoring.Transformers;
using SharpRefactoring.Visitors;
namespace SharpRefactoring
{
public class ExtractMethodCommand : AbstractRefactoringCommand
{
protected override void Run(ICSharpCode.TextEditor.TextEditorControl textEditor, ICSharpCode.SharpDevelop.Dom.Refactoring.RefactoringProvider provider)
{
if (textEditor.ActiveTextAreaControl.SelectionManager.HasSomethingSelected)
{
MethodExtractorBase extractor = GetCurrentExtractor(textEditor);
if (extractor.Extract()) {
ExtractMethodForm form = new ExtractMethodForm("NewMethod", extractor.CreatePreview());
if (form.ShowDialog() == DialogResult.OK) {
extractor.ExtractedMethod.Name = form.Text;
textEditor.Document.UndoStack.StartUndoGroup();
extractor.InsertAfterCurrentMethod();
extractor.InsertCall();
textEditor.Document.FormattingStrategy.IndentLines(textEditor.ActiveTextAreaControl.TextArea, 0, textEditor.Document.TotalNumberOfLines - 1);
textEditor.Document.UndoStack.EndUndoGroup();
textEditor.ActiveTextAreaControl.SelectionManager.ClearSelection();
}
}
}
}
MethodExtractorBase GetCurrentExtractor(TextEditorControl editor)
{
switch (ProjectService.CurrentProject.Language) {
case "C#":
return new CSharpMethodExtractor(editor, editor.ActiveTextAreaControl.SelectionManager.SelectionCollection[0]);
default:
throw new NotSupportedException("Extracting methods in the current language is not supported!");
}
}
}
}

4
src/AddIns/Misc/SharpRefactoring/Src/Forms/ExtractMethodForm.cs

@ -28,7 +28,9 @@ namespace SharpRefactoring.Forms @@ -28,7 +28,9 @@ namespace SharpRefactoring.Forms
this.txtName.Text = name;
this.txtPreview.Text = preview;
txtName_TextChanged(null, EventArgs.Empty);
this.txtName.SelectAll();
}

302
src/AddIns/Misc/SharpRefactoring/Src/MethodExtractorBase.cs

@ -0,0 +1,302 @@ @@ -0,0 +1,302 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="sie_pam@gmx.at"/>
// <version>$Revision: 3287 $</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.PrettyPrinter;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.SharpDevelop;
using ICSharpCode.TextEditor.Document;
using SharpRefactoring.Visitors;
using Dom = ICSharpCode.SharpDevelop.Dom;
namespace SharpRefactoring
{
/// <summary>
/// Description of MethodExtractorBase.
/// </summary>
public class MethodExtractorBase
{
protected ICSharpCode.TextEditor.TextEditorControl textEditor;
protected ISelection currentSelection;
protected IDocument currentDocument;
protected MethodDeclaration extractedMethod;
protected ParametrizedNode parentNode;
protected Statement caller;
protected List<LocalVariableDeclaration> beforeCallDeclarations;
protected IOutputAstVisitor output;
protected VariableDeclaration returnedVariable;
protected List<ISpecial> specialsList;
public Statement Caller {
get { return caller; }
}
public MethodDeclaration ExtractedMethod {
get { return extractedMethod; }
}
public MethodExtractorBase(ICSharpCode.TextEditor.TextEditorControl textEditor, ISelection selection, IOutputAstVisitor output)
{
this.currentDocument = textEditor.Document;
this.textEditor = textEditor;
this.currentSelection = selection;
this.output = output;
}
protected static Statement CreateCaller(ParametrizedNode parent, MethodDeclaration method, VariableDeclaration returnVariable)
{
Statement caller;
InvocationExpression expr = new InvocationExpression(new IdentifierExpression(method.Name), CreateArgumentExpressions(method.Parameters));
if (method.TypeReference.Type != "void") {
if (parent is MethodDeclaration) {
if (method.TypeReference == (parent as MethodDeclaration).TypeReference)
caller = new ReturnStatement(expr);
else {
returnVariable.Initializer = expr;
caller = new LocalVariableDeclaration(returnVariable);
}
}
else {
returnVariable.Initializer = expr;
caller = new LocalVariableDeclaration(returnVariable);
}
}
else {
caller = new ExpressionStatement(expr);
}
return caller;
}
protected void CreateReturnStatement(MethodDeclaration newMethod, List<VariableDeclaration> possibleReturnValues)
{
HasReturnStatementVisitor hrsv = new HasReturnStatementVisitor();
newMethod.AcceptVisitor(hrsv, null);
if (hrsv.HasReturn) {
if (this.parentNode is MethodDeclaration) newMethod.TypeReference = (this.parentNode as MethodDeclaration).TypeReference;
if (this.parentNode is PropertyDeclaration) newMethod.TypeReference = (this.parentNode as PropertyDeclaration).TypeReference;
if (this.parentNode is OperatorDeclaration) newMethod.TypeReference = (this.parentNode as OperatorDeclaration).TypeReference;
}
else {
if (possibleReturnValues.Count > 0) {
newMethod.TypeReference = possibleReturnValues[possibleReturnValues.Count - 1].TypeReference;
newMethod.Body.Children.Add(new ReturnStatement(new IdentifierExpression(possibleReturnValues[possibleReturnValues.Count - 1].Name)));
}
else newMethod.TypeReference = new TypeReference("void");
}
if (newMethod.TypeReference.Type == "void") {
this.returnedVariable = null;
} else {
this.returnedVariable = possibleReturnValues[possibleReturnValues.Count - 1];
}
}
public string CreatePreview()
{
BlockStatement body = this.extractedMethod.Body;
this.extractedMethod.Body = new BlockStatement();
this.extractedMethod.AcceptVisitor(output, null);
this.extractedMethod.Body = body;
return output.Text;
}
public void InsertCall()
{
string call = GenerateCode(CreateCaller(this.parentNode, this.extractedMethod, this.returnedVariable), false);
StringBuilder builder = new StringBuilder();
foreach (LocalVariableDeclaration v in this.beforeCallDeclarations) {
builder.AppendLine(GenerateCode(v, false));
}
this.currentDocument.Replace(this.currentSelection.Offset, this.currentSelection.Length, /*builder.ToString() + "\n" +*/ call);
}
public void InsertAfterCurrentMethod()
{
using (SpecialNodesInserter.Install(this.specialsList, this.output)) {
string code = "\n\n" + GenerateCode(this.extractedMethod, true);
code = code.TrimEnd('\n', ' ', '\t');
Dom.IMember p = GetParentMember(this.textEditor, this.currentSelection.StartPosition.Line, this.currentSelection.StartPosition.Column);
textEditor.Document.Insert(textEditor.Document.PositionToOffset(
new ICSharpCode.TextEditor.TextLocation(
p.BodyRegion.EndColumn - 1, p.BodyRegion.EndLine - 1)
), code);
}
}
protected static bool CheckForJumpInstructions(MethodDeclaration method, ISelection selection)
{
FindJumpInstructionsVisitor fjiv = new FindJumpInstructionsVisitor(method, selection);
method.AcceptVisitor(fjiv, null);
return fjiv.IsOk;
}
protected static bool IsInSel(Location location, ISelection sel)
{
bool result = (sel.ContainsPosition(new ICSharpCode.TextEditor.TextLocation(location.Column - 1, location.Line - 1)));
return result;
}
protected static BlockStatement GetBlock(string data)
{
data = "class Temp { public void t() {" + data + "} }";
using (IParser parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(data))) {
parser.Parse();
if (parser.Errors.Count > 0) {
MessageService.ShowError("Invalid selection! Please select a valid range!");
}
MethodDeclaration method = (MethodDeclaration)(parser.CompilationUnit.Children[0].Children[0]);
return method.Body;
}
}
protected static string GetIndentation(string line)
{
string indent = "";
foreach (char c in line) {
if ((c == ' ') || (c == '\t'))
indent += c;
else
break;
}
return indent;
}
protected static List<Expression> CreateArgumentExpressions(List<ParameterDeclarationExpression> parameters)
{
List<Expression> expressions = new List<Expression>();
foreach (ParameterDeclarationExpression pde in parameters)
{
expressions.Add(new DirectionExpression(
(FieldDirection)Enum.Parse(typeof(FieldDirection),pde.ParamModifier.ToString()),
new IdentifierExpression(pde.ParameterName)));
}
return expressions;
}
protected virtual string GenerateCode(INode unit, bool installSpecials)
{
throw new InvalidOperationException("Cannot use plain MethodExtractor, please use a language specific implementation!");
}
protected Dom.IMember GetParentMember(ICSharpCode.TextEditor.TextEditorControl textEditor, int line, int column)
{
Dom.ParseInformation parseInfo = ParserService.GetParseInformation(textEditor.FileName);
if (parseInfo != null) {
Dom.IClass c = parseInfo.MostRecentCompilationUnit.GetInnermostClass(line, column);
if (c != null) {
foreach (Dom.IMember member in c.Properties) {
if (member.BodyRegion.IsInside(line, column)) {
return member;
}
}
foreach (Dom.IMember member in c.Methods) {
if (member.BodyRegion.IsInside(line, column)) {
return member;
}
}
}
}
return null;
}
protected ParametrizedNode GetParentMember(int startLine, int startColumn, int endLine, int endColumn)
{
using (IParser parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(this.currentDocument.TextContent))) {
parser.Parse();
if (parser.Errors.Count > 0) {
MessageService.ShowError(null, parser.Errors.ErrorOutput);
return null;
}
FindMemberVisitor fmv = new FindMemberVisitor(startColumn, startLine, endColumn, endLine);
parser.CompilationUnit.AcceptVisitor(fmv, null);
return fmv.Member;
}
}
protected static bool HasOccurrencesAfter(bool caseSensitive, ParametrizedNode member, Location location, string name, Location start, Location end)
{
FindReferenceVisitor frv = new FindReferenceVisitor(caseSensitive, name, start, end);
member.AcceptVisitor(frv, null);
foreach (IdentifierExpression identifier in frv.Identifiers)
{
if (identifier.StartLocation > location)
return true;
}
return false;
}
protected bool IsInitializedVariable(bool caseSensitive, ParametrizedNode member, LocalLookupVariable variable)
{
if (!(variable.Initializer.IsNull)) {
return true;
} else {
FindReferenceVisitor frv = new FindReferenceVisitor(caseSensitive, variable.Name, variable.StartPos, variable.EndPos);
member.AcceptVisitor(frv, null);
foreach (IdentifierExpression expr in frv.Identifiers) {
if ((expr.EndLocation < new Location(currentSelection.StartPosition.Column, currentSelection.StartPosition.Line)) &&
!(expr.IsNull))
return true;
}
}
return false;
}
protected static bool HasAssignment(MethodDeclaration method, LocalLookupVariable variable)
{
HasAssignmentsVisitor hav = new HasAssignmentsVisitor(variable.Name, variable.TypeRef);
method.AcceptVisitor(hav, null);
return hav.HasAssignment;
}
public virtual bool Extract()
{
throw new InvalidOperationException("Cannot use plain MethodExtractor, please use a language specific implementation!");
}
}
}

65
src/AddIns/Misc/SharpRefactoring/Src/Transformers/ReplaceUnnecessaryVariableDeclarationsTransformer.cs

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="sie_pam@gmx.at"/>
// <version>$Revision: 3287 $</version>
// </file>
using System;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
using System.Collections.Generic;
namespace SharpRefactoring.Transformers
{
/// <summary>
/// Description of ReplaceUnnecessaryVariableDeclarationsTransformer.
/// </summary>
public class ReplaceUnnecessaryVariableDeclarationsTransformer : AbstractAstTransformer
{
List<VariableDeclaration> unneededVarDecls;
public ReplaceUnnecessaryVariableDeclarationsTransformer(List<VariableDeclaration> unneededVarDecls)
{
this.unneededVarDecls = unneededVarDecls;
}
bool Contains(VariableDeclaration varDecl)
{
foreach (VariableDeclaration v in this.unneededVarDecls) {
if (v.Name == varDecl.Name) {
return true;
}
}
return false;
}
public override object VisitLocalVariableDeclaration(ICSharpCode.NRefactory.Ast.LocalVariableDeclaration localVariableDeclaration, object data)
{
bool containsAll = true;
foreach (VariableDeclaration v in localVariableDeclaration.Variables) {
if (Contains(v)) {
localVariableDeclaration.Parent.Children.Add(new ExpressionStatement(
new AssignmentExpression(new IdentifierExpression(v.Name),
AssignmentOperatorType.Assign,
v.Initializer)));
} else
containsAll = false;
}
if (containsAll)
this.RemoveCurrentNode();
return base.VisitLocalVariableDeclaration(localVariableDeclaration, data);
}
public override object VisitVariableDeclaration(ICSharpCode.NRefactory.Ast.VariableDeclaration variableDeclaration, object data)
{
if (!(variableDeclaration.Parent is LocalVariableDeclaration)) {
this.ReplaceCurrentNode(new ExpressionStatement(new AssignmentExpression(new IdentifierExpression(variableDeclaration.Name), AssignmentOperatorType.Assign, variableDeclaration.Initializer)));
}
return base.VisitVariableDeclaration(variableDeclaration, data);
}
}
}

31
src/AddIns/Misc/SharpRefactoring/Src/Visitors/FindReferenceVisitor.cs

@ -20,25 +20,48 @@ namespace SharpRefactoring.Visitors @@ -20,25 +20,48 @@ namespace SharpRefactoring.Visitors
{
List<IdentifierExpression> identifiers;
string name;
TypeReference type;
Location rangeStart, rangeEnd;
StringComparer comparer;
public List<IdentifierExpression> Identifiers {
get { return identifiers; }
}
public FindReferenceVisitor(string name, TypeReference type)
public FindReferenceVisitor(bool caseSensitive, string name, Location rangeStart, Location rangeEnd)
{
this.identifiers = new List<IdentifierExpression>();
this.name = name;
this.type = type;
this.rangeEnd = rangeEnd;
this.rangeStart = rangeStart;
this.comparer = (caseSensitive) ? StringComparer.InvariantCulture : StringComparer.InvariantCultureIgnoreCase;
}
public override object VisitIdentifierExpression(ICSharpCode.NRefactory.Ast.IdentifierExpression identifierExpression, object data)
{
if (identifierExpression.Identifier == name) {
if (Compare(identifierExpression)) {
identifiers.Add(identifierExpression);
}
return base.VisitIdentifierExpression(identifierExpression, data);
}
bool Compare(IdentifierExpression ie) {
return ((this.comparer.Compare(ie.Identifier, this.name) == 0) &&
Inside(ie.StartLocation, ie.EndLocation));
}
bool IsIn(TypeReference type, List<TypeReference> list)
{
foreach (TypeReference tr in list) {
if (tr.Type == type.Type)
return true;
}
return false;
}
bool Inside(Location start, Location end)
{
return start >= this.rangeStart && end <= this.rangeEnd;
}
}
}

Loading…
Cancel
Save