diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CAutoIndentTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CAutoIndentTest.java index 5cf597fe05f..0e96290ebe9 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CAutoIndentTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CAutoIndentTest.java @@ -65,6 +65,18 @@ public class CAutoIndentTest extends TestCase { return (IAutoEditStrategy)fStrategyMap.get(contentType); } + /** + * Empties the document, and returns the caret to the origin (0,0) + */ + public void reset() { + try { + goTo(0,0); + fDoc.set(""); + } catch(BadLocationException ble) { + fail(ble.getMessage()); + } + } + public void type(String text) throws BadLocationException { for (int i = 0; i < text.length(); ++i) { type(text.charAt(i)); @@ -396,4 +408,84 @@ public class CAutoIndentTest extends TestCase { assertEquals("\t// int f;", tester.getLine(0)); //$NON-NLS-1$ } + /** + * Tests brackets with semi-colons are inserted in the appropriate + * contexts + * @throws BadLocationException + */ + public void testBracketWithSemiColonInsertion() throws BadLocationException { + AutoEditTester tester = createAutoEditTester(); + String[] kw= new String[] {"class", "union", "struct", "enum"}; + String[] kw_inh= new String[] {"class", "union", "struct", "enum"}; + + for(int i=0; iaddActionListener(\n" + - " new ActionListener() {\n" - ); - - String indent= fScanner.computeIndentation(fDocument.getLength()).toString(); - Assert.assertEquals(" ", indent); - } - - public void testAnonymousIndentation2() { - fDocument.set( " MenuItem mi= new MenuItem(\"About...\");\n" + - " mi->addActionListener(\n" + - " new ActionListener() {\n" + - " public void actionPerformed(ActionEvent event) {\n" + - " about();\n" + - " }\n" + - " }\n" + - ");" - ); - - // this is bogus, since this is really just an unfinished call argument list - how could we know - String indent= fScanner.computeIndentation(fDocument.getLength() - 2).toString(); - Assert.assertEquals(" ", indent); - } - - public void testExceptionIndentation1() { - fDocument.set("public void processChildren(CompositeExpression result, IConfigurationElement element) throws CoreException {\n" + - " IConfigurationElement[] children= element.getChildren();\n" + - " if (children != null) {\n" + - " for (int i= 0; i < children.length; i++) {\n" + - " Expression child= parse(children[i]);\n" + - " if (child == null)\n" + - " new Bla(new CoreExeption(new Status(IStatus.ERROR, JavaPlugin.getPluginId()"); - - String indent= fScanner.computeIndentation(fDocument.getLength()).toString(); - Assert.assertEquals(" ", indent); - } - - public void testExceptionIndentation2() { - fDocument.set("public void processChildren(CompositeExpression result, IConfigurationElement element) throws CoreException {\n" + - " IConfigurationElement[] children= element.getChildren();\n" + - " if (children != null) {\n" + - " for (int i= 0; i < children.length; i++) {\n" + - " Expression child= parse(children[i]);\n" + - " if (child == null)\n" + - " new Bla(new CoreExeption(new Status(IStatus.ERROR, JavaPlugin.getPluginId(),"); - - String indent= fScanner.computeIndentation(fDocument.getLength()).toString(); - Assert.assertEquals(" ", indent); - } - - public void testExceptionIndentation3() { - fDocument.set("public void processChildren(CompositeExpression result, IConfigurationElement element) throws CoreException {\n" + - " IConfigurationElement[] children= element.getChildren();\n" + - " if (children != null) {\n" + - " for (int i= 0; i < children.length; i++) {\n" + - " Expression child= parse(children[i]);\n" + - " if (child == null)\n" + - " new char[] { new CoreExeption(new Status(IStatus.ERROR, JavaPlugin.getPluginId(),"); - - String indent= fScanner.computeIndentation(fDocument.getLength()).toString(); - Assert.assertEquals(" ", indent); - } - public void testListAlignmentMethodDeclaration() { // parameter declaration - alignment with parenthesis fDocument.set( "\tvoid proc ( int par1, int par2,\n" + @@ -801,25 +736,13 @@ public class CHeuristicScannerTest extends TestCase { fDocument.set( " switch (i) {\n" + " case 1:\n" + - " new Runnable() {\n" + + " do {\n" + ""); String indent= fScanner.computeIndentation(fDocument.getLength()).toString(); Assert.assertEquals(" ", indent); } - public void testAnonymousTypeBraceNextLine() throws Exception { - fDocument.set( - " MenuItem mi= new MenuItem(\"About...\");\n" + - " mi->addActionListener(new ActionListener() " + - " {\n" - ); - - String indent= fScanner.computeIndentation(fDocument.getLength() - 2).toString(); - Assert.assertEquals(" ", indent); - - } - public void testClassInstanceCreationHeuristic() throws Exception { fDocument.set(" method(new std::vector(10), foo, new int[])"); @@ -840,6 +763,46 @@ public class CHeuristicScannerTest extends TestCase { for (int offset= 57; offset < 59; offset++) assertFalse(fHeuristicScanner.looksLikeClassInstanceCreationBackward(offset, CHeuristicScanner.UNBOUND)); } + + public void testFieldReferenceHeuristic() throws Exception { + fDocument.set("t.f=tp->f-T::f;"); + for (int offset= 0; offset < 2; offset++) + assertFalse(fHeuristicScanner.looksLikeFieldReferenceBackward(offset, CHeuristicScanner.UNBOUND)); + for (int offset= 2; offset < 4; offset++) + assertTrue(fHeuristicScanner.looksLikeFieldReferenceBackward(offset, CHeuristicScanner.UNBOUND)); + for (int offset= 4; offset < 8; offset++) + assertFalse(fHeuristicScanner.looksLikeFieldReferenceBackward(offset, CHeuristicScanner.UNBOUND)); + for (int offset= 8; offset < 10; offset++) + assertTrue(fHeuristicScanner.looksLikeFieldReferenceBackward(offset, CHeuristicScanner.UNBOUND)); + for (int offset= 10; offset < 13; offset++) + assertFalse(fHeuristicScanner.looksLikeFieldReferenceBackward(offset, CHeuristicScanner.UNBOUND)); + for (int offset= 13; offset < 14; offset++) + assertTrue(fHeuristicScanner.looksLikeFieldReferenceBackward(offset, CHeuristicScanner.UNBOUND)); + for (int offset= 15; offset < 15; offset++) + assertFalse(fHeuristicScanner.looksLikeFieldReferenceBackward(offset, CHeuristicScanner.UNBOUND)); + } + + public void testCompositeTypeDefinitionHeuristic() throws Exception { + int offset; + fDocument.set("class A {"); + offset= fDocument.get().indexOf("{"); + assertTrue(fHeuristicScanner.looksLikeCompositeTypeDefinitionBackward(offset, CHeuristicScanner.UNBOUND)); + fDocument.set("class A : B {"); + offset= fDocument.get().indexOf("{"); + assertTrue(fHeuristicScanner.looksLikeCompositeTypeDefinitionBackward(offset, CHeuristicScanner.UNBOUND)); + fDocument.set("struct A : B {"); + offset= fDocument.get().indexOf("{"); + assertTrue(fHeuristicScanner.looksLikeCompositeTypeDefinitionBackward(offset, CHeuristicScanner.UNBOUND)); + fDocument.set("class A : virtual public B {"); + offset= fDocument.get().indexOf("{"); + assertTrue(fHeuristicScanner.looksLikeCompositeTypeDefinitionBackward(offset, CHeuristicScanner.UNBOUND)); + fDocument.set("class A : public B, protected virtual C {"); + offset= fDocument.get().indexOf("{"); + assertTrue(fHeuristicScanner.looksLikeCompositeTypeDefinitionBackward(offset, CHeuristicScanner.UNBOUND)); + fDocument.set("template class A : public B, protected C {"); + offset= fDocument.get().indexOf("{"); + assertTrue(fHeuristicScanner.looksLikeCompositeTypeDefinitionBackward(offset, CHeuristicScanner.UNBOUND)); + } public void testShiftOperator() throws Exception { fDocument.set( diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAutoIndentStrategy.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAutoIndentStrategy.java index 95e571b954e..9aed3051335 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAutoIndentStrategy.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAutoIndentStrategy.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2006 IBM Corporation and others. + * Copyright (c) 2000, 2007 IBM Corporation 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 @@ -303,6 +303,13 @@ public class CAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy { if (reference != null) buf.append(reference); buf.append('}'); + int bound= c.offset > 200 ? c.offset - 200 : CHeuristicScanner.UNBOUND; + int bracePos = scanner.findOpeningPeer(c.offset - 1, bound, '{', '}'); + if (bracePos != CHeuristicScanner.NOT_FOUND) { + if (scanner.looksLikeCompositeTypeDefinitionBackward(bracePos, bound)) { + buf.append(';'); + } + } } // insert extra line upon new line between two braces else if (c.offset > start && contentStart < lineEnd && d.getChar(contentStart) == '}') { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java index a32a00188d7..4a7d78e4368 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java @@ -479,6 +479,8 @@ public final class CHeuristicScanner implements Symbols { return TokenCLASS; if ("while".equals(s)) //$NON-NLS-1$ return TokenWHILE; + if ("union".equals(s)) //$NON-NLS-1$ + return TokenUNION; break; case 6: if ("delete".equals(s)) //$NON-NLS-1$ @@ -499,6 +501,8 @@ public final class CHeuristicScanner implements Symbols { return TokenDEFAULT; if ("private".equals(s)) //$NON-NLS-1$ return TokenPRIVATE; + if ("virtual".equals(s)) //$NON-NLS-1$ + return TokenVIRTUAL; break; case 9: if ("protected".equals(s)) //$NON-NLS-1$ @@ -867,17 +871,17 @@ public final class CHeuristicScanner implements Symbols { * true if start is at the following positions (|): * *
-	 *  new new std::vector<std::string>|(10)
-	 *  new new str_vector |(10)
-	 *  new new / * comment * / str_vector |(10)
+	 *  new std::vector<std::string>|(10)
+	 *  new str_vector |(10)
+	 *  new / * comment * / str_vector |(10)
 	 * 
* * but not the following: * *
-	 *  new new std::vector<std::string>(10)|
-	 *  new new std::vector<std::string>|(10)
-	 *  new new vector (10)|
+	 *  new std::vector<std::string>(10)|
+	 *  new std::vector<std::string>|(10)
+	 *  new vector (10)|
 	 *  vector |(10)
 	 * 
* @@ -907,8 +911,8 @@ public final class CHeuristicScanner implements Symbols { /** * Returns true if the document, when scanned backwards from start - * appears to contain a field reference, i.e. a (optional) name preceded by a . or -> - * The start must be before the operator. + * appears to contain a field reference, i.e. a (optional) name preceded by a . + * or -> or ::. * * @param start the position after the field reference operator. * @param bound the first position in fDocument to not consider any more, with @@ -928,7 +932,129 @@ public final class CHeuristicScanner implements Symbols { if (token == Symbols.TokenMINUS) { return true; } + } else if (token == Symbols.TokenCOLON) { + token= previousToken(getPosition(), bound); + if (token == Symbols.TokenCOLON) { + return true; + } } return false; } + + /** + * Returns true if the document, when scanned backwards from start + * appears to be a composite type (class, struct, union) or enum definition. Examples: + * + *
+	 * class A {
+	 * struct A {
+	 * class A : B {
+	 * class A : virtual public B, protected C<T> {
+	 * enum E {
+	 * 
+ * + * @param start the position of the opening brace. + * @param bound the first position in fDocument to not consider any more, with + * bound < start, or UNBOUND + * @return true if the current position looks like a composite type definition + */ + public boolean looksLikeCompositeTypeDefinitionBackward(int start, int bound) { + int token= previousToken(start - 1, bound); + if (token == Symbols.TokenIDENT) { + token= previousToken(getPosition(), bound); + switch (token) { + case Symbols.TokenCLASS: + case Symbols.TokenSTRUCT: + case Symbols.TokenUNION: + case Symbols.TokenENUM: + return true; // no base-clause + default: + // backtrack + token= previousToken(start - 1, bound); + break; + } + } + // match base-clause + if (token == Symbols.TokenGREATERTHAN) { + findOpeningPeer(getPosition(), bound, '<', '>'); + token= previousToken(getPosition(), bound); + if (token != Symbols.TokenLESSTHAN) { + return false; + } + token= previousToken(getPosition(), bound); + } + outer: while (token == Symbols.TokenIDENT) {// type name or base type + token= previousToken(getPosition(), bound); + // match nested-name-specifier + while (token == Symbols.TokenCOLON) { // colon of qualification + token= previousToken(getPosition(), bound); + if (token != Symbols.TokenCOLON) { // second colon of qualification + break outer; + } + token= previousToken(getPosition(), bound); + if (token != Symbols.TokenIDENT) // qualification name? + break; + token= previousToken(getPosition(), bound); + } + switch (token) { + case Symbols.TokenVIRTUAL: + token= previousToken(getPosition(), bound); + /* fallthrough */ + case Symbols.TokenPUBLIC: + case Symbols.TokenPROTECTED: + case Symbols.TokenPRIVATE: + token= previousToken(getPosition(), bound); + if (token == Symbols.TokenVIRTUAL) { + token= previousToken(getPosition(), bound); + } + if (token == Symbols.TokenCOMMA) { + token= previousToken(getPosition(), bound); + if (token == Symbols.TokenGREATERTHAN) { + findOpeningPeer(getPosition(), bound, '<', '>'); + token= previousToken(getPosition(), bound); + if (token != Symbols.TokenLESSTHAN) { + return false; + } + token= previousToken(getPosition(), bound); + } + continue; // another base type + } + if (token != Symbols.TokenCOLON) // colon after class def identifier + return false; + /* fallthrough */ + case Symbols.TokenCOLON: + token= previousToken(getPosition(), bound); + break outer; + case Symbols.TokenCOMMA: + token= previousToken(getPosition(), bound); + if (token == Symbols.TokenGREATERTHAN) { + findOpeningPeer(getPosition(), bound, '<', '>'); + token= previousToken(getPosition(), bound); + if (token != Symbols.TokenLESSTHAN) { + return false; + } + token= previousToken(getPosition(), bound); + } + continue; // another base type + case Symbols.TokenIDENT: + break outer; + default: + return false; + } + } + if (token != Symbols.TokenIDENT) { + return false; + } + token= previousToken(getPosition(), bound); + switch (token) { + case Symbols.TokenCLASS: + case Symbols.TokenSTRUCT: + case Symbols.TokenUNION: + case Symbols.TokenENUM: // enum is actually not valid here + return true; + default: + return false; + } + } + } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/Symbols.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/Symbols.java index 6dc62e2a1ea..a4ad2275e76 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/Symbols.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/Symbols.java @@ -54,6 +54,8 @@ public interface Symbols { int TokenDELETE= 1027; int TokenCLASS= 1028; int TokenSTRUCT= 1029; - int TokenENUM= 1030; + int TokenUNION= 1030; + int TokenENUM= 1031; + int TokenVIRTUAL= 1032; int TokenIDENT= 2000; }