diff --git a/core/org.eclipse.cdt.ui.tests/resources/refactoring/ExtractLocalVariable.rts b/core/org.eclipse.cdt.ui.tests/resources/refactoring/ExtractLocalVariable.rts new file mode 100644 index 00000000000..8316d3a1f58 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/resources/refactoring/ExtractLocalVariable.rts @@ -0,0 +1,252 @@ +//!ExtractLocalVariableRefactoringTest int +//#org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVariableRefactoringTest +//@A.h +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + virtual ~A(); + int foo(); +}; + +#endif /*A_H_*/ + +//= +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + virtual ~A(); + int foo(); +}; + +#endif /*A_H_*/ + +//@A.cpp +#include "A.h" + +A::A() +{ +} + +A::~A() +{ +} + +int A::foo() +{ + return /*$*/42/*$$*/; +} + +//= +#include "A.h" + +A::A() +{ +} + +A::~A() +{ +} + +int A::foo() +{ + int i = 42; + return i; +} + +//!ExtractLocalVariableRefactoringTest char +//#org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVariableRefactoringTest +//@A.h +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + virtual ~A(); + int foo(); +}; + +#endif /*A_H_*/ + +//= +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + virtual ~A(); + int foo(); +}; + +#endif /*A_H_*/ + +//@A.cpp +#include "A.h" + +A::A() +{ +} + +A::~A() +{ +} + +int A::foo() +{ + return /*$*/'c'/*$$*/; +} + +//= +#include "A.h" + +A::A() +{ +} + +A::~A() +{ +} + +int A::foo() +{ + char temp = 'c'; + return temp; +} + +//!ExtractLocalVariableRefactoringTest float +//#org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVariableRefactoringTest +//@A.h +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + virtual ~A(); + float foo(); +}; + +#endif /*A_H_*/ + +//= +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + virtual ~A(); + float foo(); +}; + +#endif /*A_H_*/ + +//@A.cpp +#include "A.h" + +A::A() +{ +} + +A::~A() +{ +} + +float A::foo() +{ + return /*$*/42.0/*$$*/; +} + +//= +#include "A.h" + +A::A() +{ +} + +A::~A() +{ +} + +float A::foo() +{ + float f = 42.0; + return f; +} + +//!ExtractLocalVariableRefactoringTest parentheses +//#org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVariableRefactoringTest +//@A.h +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + virtual ~A(); + int foo(); +}; + +#endif /*A_H_*/ + +//= +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + virtual ~A(); + int foo(); +}; + +#endif /*A_H_*/ + +//@A.cpp +#include "A.h" + +A::A() +{ +} + +A::~A() +{ +} + +int A::foo() +{ + return /*$*/(42)/*$$*/; +} + +//= +#include "A.h" + +A::A() +{ +} + +A::~A() +{ +} + +int A::foo() +{ + int i = 42; + return i; +} + diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java index 2c575bc0d71..4e51b573700 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java @@ -15,6 +15,7 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.eclipse.cdt.ui.tests.refactoring.extractfunction.ExtractFunctionTestSuite; +import org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVariableTestSuite; import org.eclipse.cdt.ui.tests.refactoring.rename.RenameRegressionTests; import org.eclipse.cdt.ui.tests.refactoring.utils.UtilTestSuite; @@ -33,6 +34,7 @@ public class RefactoringTestSuite extends TestSuite { suite.addTest(RefactoringTester.suite("HideMethodRefactoringTests", "resources/refactoring/HideMethod.rts")); suite.addTest(RefactoringTester.suite("GettersAndSettersTests", "resources/refactoring/GenerateGettersAndSetters.rts")); suite.addTest(RefactoringTester.suite("ImplementMethodRefactoringTests", "resources/refactoring/ImplementMethod.rts")); + suite.addTest(ExtractLocalVariableTestSuite.suite()); return suite; } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringTest.java new file mode 100644 index 00000000000..c59b91e563b --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringTest.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable; + +import java.util.Properties; +import java.util.Vector; + +import org.eclipse.core.resources.IFile; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; + +import org.eclipse.cdt.ui.tests.refactoring.RefactoringTest; +import org.eclipse.cdt.ui.tests.refactoring.TestSourceFile; + +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.NameNVisibilityInformation; +import org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable.ExtractLocalVariableRefactoring; + +/** + * Test harness for Extract Local Variable refactoring tests. + * + * @author Tom Ball + */ +public class ExtractLocalVariableRefactoringTest extends RefactoringTest { + protected String variableName; + protected boolean fatalError; + + public ExtractLocalVariableRefactoringTest(String name, Vector files) { + super(name, files); + } + + @Override + protected void runTest() throws Throwable { + IFile refFile = project.getFile(fileName); + NameNVisibilityInformation info = new NameNVisibilityInformation(); + info.setName(variableName); + CRefactoring refactoring = new ExtractLocalVariableRefactoring( refFile, selection, info); + RefactoringStatus checkInitialConditions = refactoring.checkInitialConditions(NULL_PROGRESS_MONITOR); + + if(fatalError){ + assertConditionsFatalError(checkInitialConditions); + return; + } + + assertConditionsOk(checkInitialConditions); + Change createChange = refactoring.createChange(NULL_PROGRESS_MONITOR); + RefactoringStatus finalConditions = refactoring.checkFinalConditions(NULL_PROGRESS_MONITOR); + assertConditionsOk(finalConditions); + createChange.perform(NULL_PROGRESS_MONITOR); + compareFiles(fileMap); + } + + @Override + protected void configureRefactoring(Properties refactoringProperties) { + variableName = refactoringProperties.getProperty("variablename", "temp"); //$NON-NLS-1$ //$NON-NLS-2$ + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableTestSuite.java new file mode 100644 index 00000000000..dc3f7d36fb4 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableTestSuite.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.eclipse.cdt.ui.tests.refactoring.RefactoringTester; + +/** + * Test suite to run just the Extract Local Variable unit tests. + * + * @author Tom Ball + */ +public class ExtractLocalVariableTestSuite extends TestSuite { + + @SuppressWarnings("nls") + public static Test suite() throws Exception { + TestSuite suite = new ExtractLocalVariableTestSuite(); + suite.addTest(RefactoringTester.suite("ExtractLocalVariableRefactoringTests", + "resources/refactoring/ExtractLocalVariable.rts")); + return suite; + } +} diff --git a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF index b0aaf7b00ba..9b1833f3b07 100644 --- a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF @@ -31,6 +31,7 @@ Export-Package: org.eclipse.cdt.internal.corext;x-internal:=true, org.eclipse.cdt.internal.ui.refactoring;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.dialogs;x-internal:=true, org.eclipse.cdt.internal.ui.refactoring.extractconstant;x-friends:="org.eclipse.cdt.ui.tests", + org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.extractfunction;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.gettersandsetters;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.hidemethod;x-friends:="org.eclipse.cdt.ui.tests", diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties index 83eab6f9eb3..fca0323cfcd 100644 --- a/core/org.eclipse.cdt.ui/plugin.properties +++ b/core/org.eclipse.cdt.ui/plugin.properties @@ -137,6 +137,7 @@ ActionDefinition.showMacroExplorer.description= Opens a quick view for macro exp category.refactoring.description= C/C++ Refactorings category.refactoring.name = Refactor - C++ refactoringExtractConstant.label = Extract Constant... +refactoringExtractLocalVariable.label = Extract Local Variable... refactoringHideMethod.label = Hide Memeber Function... @@ -144,6 +145,8 @@ ActionDefinition.renameElement.name= Rename - Refactoring ActionDefinition.renameElement.description= Rename the selected element ActionDefinition.extractConstant.name= Extract Constant - Refactoring ActionDefinition.extractConstant.description= Extract a constant for the selected expression +ActionDefinition.extractLocalVariable.name= Extract Local Variable - Refactoring +ActionDefinition.extractLocalVariable.description= Extract a local variable for the selected expression ActionDefinition.extractFunction.name= Extract Function - Refactoring ActionDefinition.extractFunction.description= Extract a function for the selected list of expressions or statements ActionDefinition.implementMethod.name= Implement Method - Source Generation @@ -158,6 +161,7 @@ CodingActionSet.description= Action set containing coding related C/C++ actions Refactoring.menu.label= Refac&tor Refactoring.renameAction.label=Re&name... Refactoring.extractConstant.label=Extr&act Constant... +Refactoring.extractLocalVariable.label=Extract &Local Variable Refactoring.extractFunction.label=Extract &Function... Refactoring.hideMethod.label=Hide Method... Refactoring.implementMethod.label=Impl&ement Method... diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index 9ebde33fbfe..c0b3f4dae45 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -1,4 +1,3 @@ - @@ -1255,6 +1254,13 @@ id="org.eclipse.cdt.ui.actions.ExtractConstant" retarget="true"> + + + + + "org.eclipse.cdt.ui.refactor.extract.constant"). */ public static final String EXTRACT_CONSTANT= "org.eclipse.cdt.ui.refactor.extract.constant"; //$NON-NLS-1$ + + /** + * Action definition ID of the refactor -> extract local variable action + * (value "org.eclipse.cdt.ui.refactor.extract.local.variable"). + */ + public static final String EXTRACT_LOCAL_VARIABLE= "org.eclipse.cdt.ui.refactor.extract.local.variable"; //$NON-NLS-1$ /** * Action definition ID of the refactor -> extract function action (value diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoring.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoring.java new file mode 100644 index 00000000000..c823dda7f9c --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoring.java @@ -0,0 +1,378 @@ +/******************************************************************************* + * Copyright (c) 2008 Google and others. All rights reserved. This program and + * the accompanying materials are made available under the terms of the Eclipse + * Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.text.edits.TextEditGroup; + +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTDeclarationStatement; +import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.IASTInitializerExpression; +import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTStatement; +import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.rewrite.ASTRewrite; + +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarationStatement; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarator; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIdExpression; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTInitializerExpression; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTLiteralExpression; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTSimpleDeclaration; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPMethod; + +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector; +import org.eclipse.cdt.internal.ui.refactoring.NameNVisibilityInformation; +import org.eclipse.cdt.internal.ui.refactoring.NodeContainer; +import org.eclipse.cdt.internal.ui.refactoring.extractfunction.ExtractExpression; +import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper; +import org.eclipse.cdt.internal.ui.refactoring.utils.SelectionHelper; + +/** + * The main class for the Extract Local Variable refactoring. This refactoring + * differs from the Extract Constant refactoring in that any valid expression + * which can be used to initialize a local variable can be extracted. + * + * @author Tom Ball + */ +public class ExtractLocalVariableRefactoring extends CRefactoring { + private IASTExpression target = null; + private final NameNVisibilityInformation info; + private NodeContainer container; + + public ExtractLocalVariableRefactoring(IFile file, ISelection selection, + NameNVisibilityInformation info) { + super(file, selection, null); + this.info = info; + name = Messages.ExtractLocalVariable; + } + + @Override + public RefactoringStatus checkInitialConditions(IProgressMonitor pm) + throws CoreException, OperationCanceledException { + SubMonitor sm = SubMonitor.convert(pm, 9); + super.checkInitialConditions(sm.newChild(6)); + + container = findAllExpressions(); + if (container.size() < 1) { + initStatus.addFatalError(Messages.ExpressionMustBeSelected); + return initStatus; + } + + sm.worked(1); + if (isProgressMonitorCanceld(sm, initStatus)) + return initStatus; + + boolean oneMarked = region != null + && isOneMarked(container.getNodesToWrite(), region); + if (!oneMarked) { + if (target == null) { + initStatus.addFatalError(Messages.NoExpressionSelected); + } else { + initStatus.addFatalError(Messages.TooManyExpressionsSelected); + } + return initStatus; + } + sm.worked(1); + + if (isProgressMonitorCanceld(sm, initStatus)) + return initStatus; + + container.findAllNames(); + sm.worked(1); + + container.getAllAfterUsedNames(); + info.addNamesToUsedNames(findAllDeclaredNames()); + sm.worked(1); + + NodeHelper.findMethodContext(container.getNodesToWrite().get(0), + getIndex()); + sm.worked(1); + + info.setName(guessTempName()); + sm.done(); + return initStatus; + } + + private ArrayList findAllDeclaredNames() { + ArrayList names = new ArrayList(); + IASTFunctionDefinition funcDef = NodeHelper + .findFunctionDefinitionInAncestors(target); + ICPPASTCompositeTypeSpecifier comTypeSpec = + getCompositeTypeSpecifier(funcDef); + if (comTypeSpec != null) { + for (IASTDeclaration dec : comTypeSpec.getMembers()) { + if (dec instanceof IASTSimpleDeclaration) { + IASTSimpleDeclaration simpDec = (IASTSimpleDeclaration) dec; + for (IASTDeclarator decor : simpDec.getDeclarators()) { + names.add(decor.getName().getRawSignature()); + } + } + } + } + return names; + } + + private ICPPASTCompositeTypeSpecifier getCompositeTypeSpecifier( + IASTFunctionDefinition funcDef) { + if (funcDef != null) { + IBinding binding = funcDef.getDeclarator().getName() + .resolveBinding(); + if (binding instanceof CPPMethod) { + + CPPMethod method = (CPPMethod) binding; + IASTNode decl = method.getDeclarations()[0]; + + IASTNode spec = decl.getParent().getParent(); + if (spec instanceof ICPPASTCompositeTypeSpecifier) { + ICPPASTCompositeTypeSpecifier compTypeSpec = + (ICPPASTCompositeTypeSpecifier) spec; + return compTypeSpec; + } + } + } + return null; + } + + private boolean isOneMarked(List selectedNodes, + Region textSelection) { + boolean oneMarked = false; + for (IASTNode node : selectedNodes) { + if (node instanceof IASTExpression) { + IASTExpression expression = (IASTExpression) node; + boolean isInSameFileSelection = SelectionHelper + .isInSameFileSelection(textSelection, expression, file); + if (isInSameFileSelection + && isExpressionInSelection(expression, textSelection)) { + if (target == null) { + target = expression; + oneMarked = true; + } else if (!isTargetChild(expression)) { + oneMarked = false; + } + } + } + } + return oneMarked; + } + + private boolean isExpressionInSelection(IASTExpression expression, + Region selection) { + IASTFileLocation location = expression.getFileLocation(); + int e1 = location.getNodeOffset(); + int e2 = location.getNodeOffset() + location.getNodeLength(); + int s1 = selection.getOffset(); + int s2 = selection.getOffset() + selection.getLength(); + return e1 >= s1 && e2 <= s2; + } + + private boolean isTargetChild(IASTExpression child) { + if (target == null) { + return false; + } + IASTNode node = child; + while (node != null) { + if (node.getParent() == target) + return true; + node = node.getParent(); + } + return false; + } + + private NodeContainer findAllExpressions() { + final NodeContainer container = new NodeContainer(); + + unit.accept(new CPPASTVisitor() { + { + shouldVisitExpressions = true; + } + + @Override + public int visit(IASTExpression expression) { + if (SelectionHelper.isSelectedFile(region, expression, file)) { + container.add(expression); + return PROCESS_SKIP; + } + return super.visit(expression); + } + }); + + return container; + } + + @Override + protected void collectModifications(IProgressMonitor pm, + ModificationCollector collector) throws CoreException, + OperationCanceledException { + String variableName = info.getName(); + TextEditGroup editGroup = new TextEditGroup( + Messages.CreateLocalVariable); + + // Define temporary variable declaration and insert it + IASTStatement declInsertPoint = getParentStatement(target); + IASTDeclarationStatement declaration = getVariableNodes(variableName); + declaration.setParent(declInsertPoint.getParent()); + ASTRewrite rewriter = collector.rewriterForTranslationUnit(unit); + rewriter.insertBefore(declInsertPoint.getParent(), declInsertPoint, + declaration, editGroup); + + // Replace target with reference to temporary variable + CPPASTIdExpression idExpression = new CPPASTIdExpression( + new CPPASTName(variableName.toCharArray())); + rewriter.replace(target, idExpression, editGroup); + } + + private IASTStatement getParentStatement(IASTNode node) { + while (node != null) { + if (node instanceof IASTStatement) + return (IASTStatement) node; + node = node.getParent(); + } + return null; + } + + private IASTDeclarationStatement getVariableNodes(String newName) { + IASTSimpleDeclaration simple = new CPPASTSimpleDeclaration(); + + IASTDeclSpecifier declSpec = new ExtractExpression() + .determineReturnType(deblock(target), null); + declSpec.setStorageClass(IASTDeclSpecifier.sc_unspecified); + simple.setDeclSpecifier(declSpec); + + IASTDeclarator decl = new CPPASTDeclarator(); + IASTName name = new CPPASTName(newName.toCharArray()); + decl.setName(name); + + IASTInitializerExpression init = new CPPASTInitializerExpression(); + init.setExpression(deblock(target)); + decl.setInitializer(init); + simple.addDeclarator(decl); + + return new CPPASTDeclarationStatement(simple); + } + + /** + * Removes surrounding parentheses from an expression. If the expression + * does not have surrounding parentheses, the original expression is returned. + */ + private static IASTExpression deblock(IASTExpression expression) { + if (expression instanceof IASTUnaryExpression) { + IASTUnaryExpression unary = (IASTUnaryExpression)expression; + if (unary.getOperator() == IASTUnaryExpression.op_bracketedPrimary) { + return deblock(unary.getOperand()); + } + } + return expression; + } + + public String guessTempName() { + String[] proposals= guessTempNames(); + if (proposals.length == 0) + return info.getName(); + else + return proposals[0]; + } + + /** + * @return proposed variable names (may be empty, but not null). The first + * proposal should be used as "best guess" (if it exists). + */ + public String[] guessTempNames() { + final List guessedTempNames = new ArrayList(); + if (target != null) { + target.accept(new CPPASTVisitor() { + { + shouldVisitNames = true; + shouldVisitExpressions = true; + } + + @Override + public int visit(IASTName name) { + addTempName(name.getLastName().toString()); + return super.visit(name); + } + + @Override + public int visit(IASTExpression expression) { + if (expression instanceof CPPASTLiteralExpression) { + CPPASTLiteralExpression literal = (CPPASTLiteralExpression)expression; + String name = null; + char[] value = literal.getValue(); + switch (literal.getKind()) { + case IASTLiteralExpression.lk_char_constant: + name = Character.toString(value[0]); + break; + case IASTLiteralExpression.lk_float_constant: + name = "f"; //$NON-NLS-1$ + break; + case IASTLiteralExpression.lk_integer_constant: + name = "i"; //$NON-NLS-1$ + break; + case IASTLiteralExpression.lk_string_literal: + name = literal.toString(); + break; + case IASTLiteralExpression.lk_false: + case IASTLiteralExpression.lk_true: + name = "b"; //$NON-NLS-1$ + break; + } + if (name != null) { + addTempName(name); + } + } + return super.visit(expression); + } + + private void addTempName(String name) { + char[] tmpName = new char[name.length()]; + int len = 0; + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (len == 0 && Character.isJavaIdentifierStart(c)) { + tmpName[len++] = Character.toLowerCase(c); + } else if (Character.isJavaIdentifierPart(c)) { + tmpName[len++] = c; + } + } + name = new String(tmpName, 0, len); + if (name.length() > 0 && !guessedTempNames.contains(name) && + !info.getUsedNames().contains(name)) { + guessedTempNames.add(name); + } + } + }); + } + return guessedTempNames.toArray(new String[0]); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringRunner.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringRunner.java new file mode 100644 index 00000000000..19ecc480592 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringRunner.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.window.IShellProvider; +import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; + +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.NameNVisibilityInformation; +import org.eclipse.cdt.internal.ui.refactoring.RefactoringRunner; + +/** + * Extract Local Variable refactoring runner. + * + * @author Tom Ball + */ +public class ExtractLocalVariableRefactoringRunner extends RefactoringRunner { + + public ExtractLocalVariableRefactoringRunner(IFile file, + ISelection selection, IShellProvider shellProvider) { + super(file, selection, null, shellProvider); + } + + @Override + public void run() { + NameNVisibilityInformation info = new NameNVisibilityInformation(); + CRefactoring refactoring = new ExtractLocalVariableRefactoring(file, + selection, info); + ExtractLocalVariableRefactoringWizard wizard = new ExtractLocalVariableRefactoringWizard( + refactoring, info); + RefactoringWizardOpenOperation operator = new RefactoringWizardOpenOperation(wizard); + + try { + operator.run(shellProvider.getShell(), refactoring.getName()); + } catch (InterruptedException e) { + // initial condition checking got canceled by the user. + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringWizard.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringWizard.java new file mode 100644 index 00000000000..f4d05a18cef --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringWizard.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable; + +import org.eclipse.ltk.core.refactoring.Refactoring; +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; + +import org.eclipse.cdt.internal.ui.refactoring.NameNVisibilityInformation; + +/** + * The wizard page for Extract Local Variable Refactoring, creates the UI page. + * + * @author Tom Ball + */ +public class ExtractLocalVariableRefactoringWizard extends RefactoringWizard { + private InputPage page; + private final NameNVisibilityInformation info; + + public ExtractLocalVariableRefactoringWizard(Refactoring refactoring, + NameNVisibilityInformation info) { + super(refactoring, WIZARD_BASED_USER_INTERFACE); + this.info = info; + } + + @Override + protected void addUserInputPages() { + page = new InputPage(Messages.ExtractLocalVariable, info); + addPage(page); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/InputPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/InputPage.java new file mode 100644 index 00000000000..a5dc28afe67 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/InputPage.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + * Google + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable; + +import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.cdt.internal.ui.refactoring.NameNVisibilityInformation; +import org.eclipse.cdt.internal.ui.refactoring.dialogs.LabeledTextField; +import org.eclipse.cdt.internal.ui.refactoring.utils.IdentifierHelper; +import org.eclipse.cdt.internal.ui.refactoring.utils.IdentifierResult; + +/** + * Input verification page for the ExtractLocalVariable refactoring, cloned + * from org.eclipse.cdt.internal.ui.refactoring.extractconstant.InputPage. + * + * @author Tom Ball + */ +public class InputPage extends UserInputWizardPage { + private String label = Messages.VariableName; + private final NameNVisibilityInformation info; + private InputForm control; + + public InputPage(String name, NameNVisibilityInformation info) { + super(name); + this.info = info; + } + + public String getVariableName() { + return info.getName(); + } + + public void createControl(Composite parent) { + control = new InputForm(parent, label); + + setTitle(getName()); + setMessage(Messages.EnterVariableName); + setPageComplete(false); + Text nameText = control.getVariableNameText(); + nameText.setText(info.getName()); + nameText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + info.setName(control.getVariableNameText().getText()); + checkName(); + } + }); + + checkName(); + setControl(control); + } + + private void verifyName(String name) { + if (info.getUsedNames().contains(name)) { + setErrorMessage(NLS.bind(Messages.NameAlreadyDefined, name)); + setPageComplete(false); + } + } + + private void checkName() { + String methodName = control.getVariableNameText().getText(); + IdentifierResult result = IdentifierHelper + .checkIdentifierName(methodName); + if (result.isCorrect()) { + setErrorMessage(null); + setPageComplete(true); + verifyName(methodName); + } else { + setErrorMessage(NLS.bind(Messages.CheckName, result.getMessage())); + setPageComplete(false); + } + } + + private static class InputForm extends Composite { + LabeledTextField variableName; + + InputForm(Composite parent, String label) { + super(parent, SWT.NONE); + FillLayout layout = new FillLayout(SWT.HORIZONTAL); + GridData gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, + false); + gridData.horizontalAlignment = GridData.FILL; + setLayoutData(gridData); + setLayout(layout); + variableName = new LabeledTextField(this, label, ""); //$NON-NLS-1$ + } + + Text getVariableNameText() { + return variableName.getText(); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/Messages.java new file mode 100644 index 00000000000..1186b4cd3cb --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/Messages.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + * Google + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable; + +import org.eclipse.osgi.util.NLS; + +public final class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable.messages"; //$NON-NLS-1$ + + public static String CheckName; + public static String CreateLocalVariable; + public static String EnterVariableName; + public static String ExpressionMustBeSelected; + public static String ExtractLocalVariable; + public static String NameAlreadyDefined; + public static String NoExpressionSelected; + public static String ReplaceExpression; + public static String TooManyExpressionsSelected; + public static String VariableName; + + static { + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + // Do not instantiate + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/messages.properties new file mode 100644 index 00000000000..f5ea2ee018d --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/messages.properties @@ -0,0 +1,23 @@ +############################################################################### +# Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik +# Rapperswil, University of applied sciences and others +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Institute for Software - initial API and implementation +# IBM Corporation +# Google +############################################################################### +VariableName=Variable &name: +EnterVariableName=Enter a name for the new variable +CheckName=Check Name: {0} +NameAlreadyDefined=An element named ''{0}'' is already defined in this scope. +ExtractLocalVariable=Extract Local Variable +ExpressionMustBeSelected=An expression must be selected to activate this refactoring. +NoExpressionSelected=No expression selected. +TooManyExpressionsSelected=Too many expressions selected. +CreateLocalVariable=Create Local Variable +ReplaceExpression=Replace an expression diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java index 4c5e7f1f188..4168772e5ce 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java @@ -247,6 +247,13 @@ public class CdtActionConstants { * (value "org.eclipse.cdt.ui.actions.ExtractConstant"). */ public static final String EXTRACT_CONSTANT= "org.eclipse.cdt.ui.actions.ExtractConstant"; //$NON-NLS-1$ + + /** + * Refactor menu: name of standard Extract Local Variable global action + * (value "org.eclipse.cdt.ui.actions.ExtractLocalVariable"). + * @since 5.1 + */ + public static final String EXTRACT_LOCAL_VARIABLE= "org.eclipse.cdt.ui.actions.ExtractLocalVariable"; //$NON-NLS-1$ /** * Refactor menu: name of standard Hide Method global action diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/CRefactoringActionGroup.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/CRefactoringActionGroup.java index c13458f37f2..033654de6e7 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/CRefactoringActionGroup.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/CRefactoringActionGroup.java @@ -113,6 +113,7 @@ public class CRefactoringActionGroup extends ActionGroup implements ISelectionCh private String fGroupName= IWorkbenchActionConstants.GROUP_REORGANIZE; private CRenameAction fRenameAction; private RefactoringAction fExtractConstantAction; + private RefactoringAction fExtractLocalVariableAction; private RefactoringAction fExtractFunctionAction; private RefactoringAction fHideMethodAction; private RefactoringAction fImplementMethodAction; @@ -153,6 +154,10 @@ public class CRefactoringActionGroup extends ActionGroup implements ISelectionCh fExtractConstantAction.setActionDefinitionId(ICEditorActionDefinitionIds.EXTRACT_CONSTANT); fAllActions.add(fExtractConstantAction); + fExtractLocalVariableAction= new ExtractLocalVariableAction(); + fExtractLocalVariableAction.setActionDefinitionId(ICEditorActionDefinitionIds.EXTRACT_LOCAL_VARIABLE); + fAllActions.add(fExtractLocalVariableAction); + fExtractFunctionAction = new ExtractFunctionAction(); fExtractFunctionAction.setActionDefinitionId(ICEditorActionDefinitionIds.EXTRACT_FUNCTION); fAllActions.add(fExtractFunctionAction); @@ -204,6 +209,7 @@ public class CRefactoringActionGroup extends ActionGroup implements ISelectionCh super.fillActionBars(actionBar); setActionHandler(actionBar, CdtActionConstants.RENAME, fRenameAction); setActionHandler(actionBar, CdtActionConstants.EXTRACT_CONSTANT, fExtractConstantAction); + setActionHandler(actionBar, CdtActionConstants.EXTRACT_LOCAL_VARIABLE, fExtractLocalVariableAction); setActionHandler(actionBar, CdtActionConstants.EXTRACT_METHOD, fExtractFunctionAction); setActionHandler(actionBar, CdtActionConstants.HIDE_METHOD, fHideMethodAction); setActionHandler(actionBar, CdtActionConstants.IMPLEMENT_METHOD, fImplementMethodAction); @@ -236,6 +242,7 @@ public class CRefactoringActionGroup extends ActionGroup implements ISelectionCh addAction(refactorSubmenu, fRenameAction); refactorSubmenu.add(new Separator(GROUP_CODING)); addAction(refactorSubmenu, fExtractConstantAction); + addAction(refactorSubmenu, fExtractLocalVariableAction); addAction(refactorSubmenu, fExtractFunctionAction); addAction(refactorSubmenu, fHideMethodAction); refactorSubmenu.add(new Separator(GROUP_REORG2)); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/ExtractLocalVariableAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/ExtractLocalVariableAction.java new file mode 100644 index 00000000000..0c12c14025f --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/ExtractLocalVariableAction.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 Wind River Systems, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Markus Schorn - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.ui.refactoring.actions; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.window.IShellProvider; + +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.IWorkingCopy; + +import org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable.ExtractLocalVariableRefactoringRunner; + +/** + * Launches a extract local variable refactoring. + */ +public class ExtractLocalVariableAction extends RefactoringAction { + + public ExtractLocalVariableAction() { + super(Messages.ExtractLocalVariableAction_label); + } + + @Override + public void run(IShellProvider shellProvider, ICElement elem) { + } + + @Override + public void run(IShellProvider shellProvider, IWorkingCopy wc, ITextSelection selection) { + IResource res= wc.getResource(); + if (res instanceof IFile) { + new ExtractLocalVariableRefactoringRunner((IFile) res, selection, shellProvider).run(); + } + } + + @Override + public void updateSelection(ICElement elem) { + super.updateSelection(elem); + setEnabled(false); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java index ffc21b54597..e2df4f9c90e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java @@ -14,9 +14,11 @@ import org.eclipse.osgi.util.NLS; public class Messages extends NLS { private static final String BUNDLE_NAME = "org.eclipse.cdt.ui.refactoring.actions.messages"; //$NON-NLS-1$ + public static String CRefactoringActionGroup_menu; public static String CRenameAction_label; public static String ExtractConstantAction_label; + public static String ExtractLocalVariableAction_label; public static String ExtractFunctionAction_label; public static String HideMethodAction_label; public static String ImplementMethodAction_label; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/messages.properties index 3eb06335e1d..6c00c3ab274 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/messages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/messages.properties @@ -11,6 +11,7 @@ CRefactoringActionGroup_menu=Refactor CRenameAction_label=Rename... ExtractConstantAction_label=Extract Constant... +ExtractLocalVariableAction_label=Extract Local Variable... GettersAndSetters_label=Generate Getters and Setters... ImplementMethodAction_label=Implement Method... HideMethodAction_label=Hide Method...