diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java index 7c78f5a237d..d79d6067c2c 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java @@ -48,6 +48,7 @@ import org.eclipse.cdt.ui.tests.text.contentassist.CompletionTest_VariableType_N import org.eclipse.cdt.ui.tests.text.contentassist.CompletionTest_VariableType_NoPrefix; import org.eclipse.cdt.ui.tests.text.contentassist.CompletionTest_VariableType_Prefix; import org.eclipse.cdt.ui.tests.text.contentassist.ContentAssistTests; +import org.eclipse.cdt.ui.tests.text.selectiontests.SelectionTests; @@ -113,6 +114,10 @@ public class AutomatedSuite extends TestSuite { addTest( ContentAssistTests.suite() ); addTest( RegressionTestsUISuite.suite() ); + + // selection tests + addTest( SelectionTests.suite() ); + // Failed Tests addTest(CompletionFailedTest_MemberReference_Arrow_Prefix2.suite()); } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/regression/RefactoringRegressionTests.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/regression/RefactoringRegressionTests.java index 32a110729ea..eb9b31ac919 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/regression/RefactoringRegressionTests.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/regression/RefactoringRegressionTests.java @@ -132,7 +132,7 @@ public class RefactoringRegressionTests extends SearchRegressionTests { suite.addTest( new FailingTest( new RefactoringRegressionTests("testDestructor_29_72612"), 72612) ); //$NON-NLS-1$ suite.addTest( new RefactoringRegressionTests("testFunction_31") ); //$NON-NLS-1$ suite.addTest( new FailingTest( new RefactoringRegressionTests("testMethod_32_72717"),72717) ); //$NON-NLS-1$ - suite.addTest( new FailingTest( new RefactoringRegressionTests("testMethod_33_72605"),72605) ); //$NON-NLS-1$ + suite.addTest( new RefactoringRegressionTests("testMethod_33_72605") ); //$NON-NLS-1$ suite.addTest( new RefactoringRegressionTests("testMethod_34") ); //$NON-NLS-1$ suite.addTest( new FailingTest( new RefactoringRegressionTests("testMethod_35_72726"),72726) ); //$NON-NLS-1$ suite.addTest( new RefactoringRegressionTests("testMethod_39") ); //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selectiontests/SelectionTests.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selectiontests/SelectionTests.java new file mode 100644 index 00000000000..cc61aa824e8 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selectiontests/SelectionTests.java @@ -0,0 +1,205 @@ +package org.eclipse.cdt.ui.tests.text.selectiontests; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.search.DOMSearchUtil; +import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.core.testplugin.FileManager; +import org.eclipse.cdt.internal.core.dom.parser.ASTNode; +import org.eclipse.cdt.internal.core.index.sourceindexer.SourceIndexer; +import org.eclipse.cdt.internal.ui.CHelpProviderManager; +import org.eclipse.cdt.internal.ui.text.CHelpBookDescriptor; +import org.eclipse.cdt.ui.tests.text.contentassist.ContentAssistTests; +import org.eclipse.cdt.ui.text.ICHelpInvocationContext; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.TextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.part.FileEditorInput; +import org.eclipse.ui.texteditor.AbstractTextEditor; + +public class SelectionTests extends TestCase { + + static NullProgressMonitor monitor; + static IWorkspace workspace; + static IProject project; + static FileManager fileManager; + static boolean disabledHelpContributions = false; + { + //(CCorePlugin.getDefault().getCoreModel().getIndexManager()).reset(); + monitor = new NullProgressMonitor(); + + workspace = ResourcesPlugin.getWorkspace(); + + ICProject cPrj; + try { + cPrj = CProjectHelper.createCCProject("SelectionTestProject", "bin"); //$NON-NLS-1$ //$NON-NLS-2$ + + project = cPrj.getProject(); + project.setSessionProperty(SourceIndexer.activationKey,new Boolean(false)); + } catch ( CoreException e ) { + /*boo*/ + } + if (project == null) + fail("Unable to create project"); //$NON-NLS-1$ + + //Create file manager + fileManager = new FileManager(); + } + public SelectionTests() + { + super(); + } + /** + * @param name + */ + public SelectionTests(String name) + { + super(name); + } + + public static Test suite() { + TestSuite suite = new TestSuite( SelectionTests.class ); + suite.addTest( new SelectionTests("cleanupProject") ); //$NON-NLS-1$ + return suite; + } + + public void cleanupProject() throws Exception { + try{ + project.delete( true, false, monitor ); + project = null; + } catch( Throwable e ){ + /*boo*/ + } + } + + protected void tearDown() throws Exception { + if( project == null || !project.exists() ) + return; + + IResource [] members = project.members(); + for( int i = 0; i < members.length; i++ ){ + if( members[i].getName().equals( ".project" ) || members[i].getName().equals( ".cdtproject" ) ) //$NON-NLS-1$ //$NON-NLS-2$ + continue; + try{ + members[i].delete( false, monitor ); + } catch( Throwable e ){ + /*boo*/ + } + } + } + + protected IFile importFile(String fileName, String contents ) throws Exception{ + //Obtain file handle + IFile file = project.getProject().getFile(fileName); + + InputStream stream = new ByteArrayInputStream( contents.getBytes() ); + //Create file input stream + if( file.exists() ) + file.setContents( stream, false, false, monitor ); + else + file.create( stream, false, monitor ); + + fileManager.addFile(file); + + return file; + } + + protected IASTNode testF3(IFile file, int offset) { + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + IEditorPart part = null; + try { + part = page.openEditor(new FileEditorInput(file), "org.eclipse.cdt.ui.editor.CEditor"); // TODO Devin testing + } catch (PartInitException e) { + assertFalse(true); + } + + if (part instanceof AbstractTextEditor) { + ((AbstractTextEditor)part).getSelectionProvider().setSelection(new TextSelection(offset,0)); + + final IAction action = ((AbstractTextEditor)part).getAction("OpenDeclarations"); + action.run(); + + // the action above should highlight the declaration, so now retrieve it and use that selection to get the IASTName selected on the TU + ISelection sel = ((AbstractTextEditor)part).getSelectionProvider().getSelection(); + + if (sel instanceof TextSelection) { + IASTName[] names = DOMSearchUtil.getSelectedNamesFrom(file, ((TextSelection)sel).getOffset(), ((TextSelection)sel).getLength()); + + if (names.length == 0) { + assertFalse(true); + } else { + return names[0]; + } + } + } + + return null; + } + + public void testBug93281() throws Exception { + StringBuffer buffer = new StringBuffer(); + buffer.append("class Point{ \n"); //$NON-NLS-1$ + buffer.append("public: \n"); //$NON-NLS-1$ + buffer.append("Point(): xCoord(0){} \n"); //$NON-NLS-1$ + buffer.append("Point& operator=(const Point &rhs){return *this;} // line A\n"); //$NON-NLS-1$ + buffer.append("void* operator new [ ] (unsigned int);\n"); //$NON-NLS-1$ + buffer.append("private: \n"); //$NON-NLS-1$ + buffer.append("int xCoord; \n"); //$NON-NLS-1$ + buffer.append("}; \n"); //$NON-NLS-1$ + buffer.append("static const Point zero;\n"); //$NON-NLS-1$ + buffer.append("int main(int argc, char **argv) { \n"); //$NON-NLS-1$ + buffer.append("Point *p2 = new Point(); \n"); //$NON-NLS-1$ + buffer.append("p2-> operator // /* operator */ // F3 in the middle \n"); //$NON-NLS-1$ + buffer.append("//of \"operator\" should work\n"); //$NON-NLS-1$ + buffer.append("// \\n"); //$NON-NLS-1$ + buffer.append("/* */\n"); //$NON-NLS-1$ + buffer.append("=(zero); // line B\n"); //$NON-NLS-1$ + buffer.append("p2->operator /* oh yeah */ new // F3 in the middle of \"operator\"\n"); //$NON-NLS-1$ + buffer.append("// should work\n"); //$NON-NLS-1$ + buffer.append("//\n"); //$NON-NLS-1$ + buffer.append("[ /* sweet */ ] //\n"); //$NON-NLS-1$ + buffer.append("(2);\n"); //$NON-NLS-1$ + buffer.append("return (0); \n"); //$NON-NLS-1$ + buffer.append("}\n"); //$NON-NLS-1$ + + String code = buffer.toString(); + IFile file = importFile("test93281.cpp", code); //$NON-NLS-1$ + + int offset = code.indexOf("p2->operator") + 6; //$NON-NLS-1$ + IASTNode node = testF3(file, offset); + + assertTrue(node instanceof IASTName); + assertEquals(((IASTName)node).toString(), "operator new[]"); //$NON-NLS-1$ + assertEquals(((ASTNode)node).getOffset(), 183); + assertEquals(((ASTNode)node).getLength(), 16); + + offset = code.indexOf("p2-> operator") + 11; //$NON-NLS-1$ + node = testF3(file, offset); + + assertTrue(node instanceof IASTName); + assertEquals(((IASTName)node).toString(), "operator ="); //$NON-NLS-1$ + assertEquals(((ASTNode)node).getOffset(), 121); + assertEquals(((ASTNode)node).getLength(), 9); + + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/SelectionParseAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/SelectionParseAction.java index 3c6673a3da2..8626912c029 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/SelectionParseAction.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/SelectionParseAction.java @@ -41,6 +41,7 @@ import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchSite; import org.eclipse.ui.texteditor.IDocumentProvider; @@ -50,7 +51,8 @@ import org.eclipse.ui.texteditor.IDocumentProvider; * Created on Jun 2, 2004 */ public class SelectionParseAction extends Action { - protected static final String CSEARCH_OPERATION_TOO_MANY_NAMES_MESSAGE = "CSearchOperation.tooManyNames.message"; //$NON-NLS-1$ + private static final String OPERATOR = "operator"; //$NON-NLS-1$ + protected static final String CSEARCH_OPERATION_TOO_MANY_NAMES_MESSAGE = "CSearchOperation.tooManyNames.message"; //$NON-NLS-1$ protected static final String CSEARCH_OPERATION_NO_NAMES_SELECTED_MESSAGE = "CSearchOperation.noNamesSelected.message"; //$NON-NLS-1$ protected static final String CSEARCH_OPERATION_OPERATION_UNAVAILABLE_MESSAGE = "CSearchOperation.operationUnavailable.message"; //$NON-NLS-1$ @@ -218,8 +220,27 @@ public class SelectionParseAction extends Action { SelSearchNode sel = new SelSearchNode(); + boolean selectedOperator=false; + if (selectedWord != null && selectedWord.indexOf(OPERATOR) >= 0 && fPos >= fStartPos + selectedWord.indexOf(OPERATOR) && fPos < fStartPos + selectedWord.indexOf(OPERATOR) + OPERATOR.length()) { + selectedOperator=true; + } + + // if the operator was selected, get its proper bounds + if (selectedOperator && fEditor.getEditorInput() instanceof IFileEditorInput && + CoreModel.hasCCNature(((IFileEditorInput)fEditor.getEditorInput()).getFile().getProject())) { + int actualStart=fStartPos + selectedWord.indexOf(OPERATOR); + int actualEnd=getOperatorActualEnd(doc, fStartPos + selectedWord.indexOf(OPERATOR) + OPERATOR.length()); + + actualEnd=(actualEnd>0?actualEnd:fEndPos); + + try { + sel.selText = doc.get(actualStart, actualEnd - actualStart); + } catch (BadLocationException e) {} + sel.selStart = actualStart; + sel.selEnd = actualEnd; + // TODO Devin this only works for definitions of destructors right now // if there is a destructor and the cursor is in the destructor name's segment then get the entire destructor - if (selectedWord != null && selectedWord.indexOf('~') >= 0 && fPos - 2 >= fStartPos + selectedWord.lastIndexOf(new String(Keywords.cpCOLONCOLON))) { + } else if (selectedWord != null && selectedWord.indexOf('~') >= 0 && fPos - 2 >= fStartPos + selectedWord.lastIndexOf(new String(Keywords.cpCOLONCOLON))) { int tildePos = selectedWord.indexOf('~'); int actualStart=fStartPos + tildePos; int length=0; @@ -259,6 +280,211 @@ public class SelectionParseAction extends Action { return sel; } + private int getOperatorActualEnd(IDocument doc, int index) { + char c1, c2; + int actualEnd=-1; + boolean multiComment=false; + boolean singleComment=false; + int possibleEnd=-1; + while (actualEnd==-1) { + try { + c1=doc.getChar(index); + c2=doc.getChar(index+1); + + // skip anything within a single-line comment + if (singleComment) { + char c3=doc.getChar(index-1); + if (c3 != '\\' && (c1 == '\n' || c1 == '\r' && c2 == '\n' )) { + singleComment=false; + } + index++; + continue; + } + // skip anything within a multi-line comment + if (multiComment) { + if (c1 == '*' && c2 == '/') { + multiComment=false; + index+=2; + continue; + } + index++; + continue; + } + + switch(c1) { + case '+': { + switch(c2) { + case '=': + case '+': + actualEnd=index+2; + break; + default: + actualEnd=index+1; + break; + } + break; + } + case '-': { + switch(c2) { + case '=': + actualEnd=index+2; + break; + case '-': + switch(doc.getChar(index+2)) { + case '>': { + switch(doc.getChar(index+3)) { + case '*': + actualEnd=index+4; + break; + default: + actualEnd=index+3; + break; + } + break; + } + default: + actualEnd=index+2; + break; + } + break; + default: + + break; + } + break; + } + case '|': { + switch(c2) { + case '=': + case '|': + actualEnd=index+2; + break; + default: + actualEnd=index+1; + break; + } + break; + } + case '&': { + switch(c2) { + case '=': + case '&': + actualEnd=index+2; + break; + default: + actualEnd=index+1; + break; + } + break; + } + case '/': { + switch(c2) { + case '/': + singleComment=true; + index+=2; + break; + case '*': + multiComment=true; + index+=2; + break; + case '=': + actualEnd=index+2; + break; + default: + actualEnd=index+1; + break; + } + break; + } + case '*': + case '%': + case '^': + case '!': + case '=': { + switch(c2) { + case '=': + actualEnd=index+2; + break; + default: + actualEnd=index+1; + break; + } + break; + } + case '(': { + if (possibleEnd > 0) + actualEnd = possibleEnd; + break; + } + case ']': + case ')': + case ',': + case '~': { + actualEnd=index+1; + break; + } + case '<': { + switch(c2) { + case '=': + case '<': + switch(doc.getChar(index+2)) { + case '=': + actualEnd=index+3; + break; + default: + actualEnd=index+2; + break; + } + break; + default: + actualEnd=index; + break; + } + break; + } + case '>': { + switch(c2) { + case '=': + case '>': + switch(doc.getChar(index+2)) { + case '=': + actualEnd=index+3; + break; + default: + actualEnd=index+2; + break; + } + break; + default: + actualEnd=index; + break; + } + break; + } + case 'n': { // start of "new" + while (doc.getChar(++index) != 'w'); + possibleEnd=++index; + break; + } + case 'd': { // start of "delete" + while (doc.getChar(++index) != 't' && doc.getChar(index+1) != 'e'); + index+=2; + possibleEnd=index; + break; + } + default: + index++; + break; + } + } catch (BadLocationException e) { + // something went wrong + return -1; + } + } + + return actualEnd; + } + /** * Return the selected string from the editor * @return The string currently selected, or null if there is no valid selection