1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-09 01:05:38 +02:00

Fix for 178026: insert semi-colon when auto-inserting closing brace for class, union, struct and enum

This commit is contained in:
Anton Leherbauer 2007-03-23 11:36:27 +00:00
parent d09020f2e4
commit 92a86b658e
5 changed files with 278 additions and 88 deletions

View file

@ -65,6 +65,18 @@ public class CAutoIndentTest extends TestCase {
return (IAutoEditStrategy)fStrategyMap.get(contentType); 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 { public void type(String text) throws BadLocationException {
for (int i = 0; i < text.length(); ++i) { for (int i = 0; i < text.length(); ++i) {
type(text.charAt(i)); type(text.charAt(i));
@ -396,4 +408,84 @@ public class CAutoIndentTest extends TestCase {
assertEquals("\t// int f;", tester.getLine(0)); //$NON-NLS-1$ 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; i<kw.length; i++) {
tester.reset();
tester.type("\n\n\n "+kw[i]+" A {\n"); //$NON-NLS-1$
assertEquals("\n\n\n "+kw[i]+" A {\n\t \n };", tester.fDoc.get()); //$NON-NLS-1$
} }
for(int i=0; i<kw.length; i++) {
tester.reset();
tester.type(kw[i]+" A {\n"); //$NON-NLS-1$
assertEquals(kw[i]+" A {\n\t\r\n};", tester.fDoc.get()); //$NON-NLS-1$
}
for(int i=0; i<kw.length; i++) {
tester.reset();
tester.type("\n\n\n "+kw[i]+" A {\n"); //$NON-NLS-1$
assertEquals("\n\n\n "+kw[i]+" A {\n\t \n };", tester.fDoc.get()); //$NON-NLS-1$
}
for(int i=0; i<kw.length; i++) {
tester.reset();
tester.type("\n// foo\n\n\n//bar\n\n"); //$NON-NLS-1$
tester.goTo(2,0);
tester.type(kw[i]+" A {\n"); //$NON-NLS-1$
assertEquals("\n// foo\n"+kw[i]+" A {\n\t\n};\n\n//bar\n\n", tester.fDoc.get()); //$NON-NLS-1$
}
// this tests for a sensible behaviour for enums, although the
// code generated is invalid, its the user entered part that is
// the problem
for(int i=0; i<kw.length; i++) {
tester.reset();
tester.type("\n\n\n"+kw[i]+" A\n:\npublic B\n,\npublic C\n{\n"); //$NON-NLS-1$
assertEquals("\n\n\n"+kw[i]+" A\n:\n\tpublic B\n\t,\n\tpublic C\n\t{\n\t\n\t};", tester.fDoc.get()); //$NON-NLS-1$
}
for(int i=0; i<kw.length; i++) {
tester.reset();
tester.type("\n// foo\n\n\n//bar\n\n"); //$NON-NLS-1$
tester.goTo(2,0);
tester.type(kw[i]+" /* for(int i=0; i<100; i++) {} */\nA \n{\n"); //$NON-NLS-1$
assertEquals("\n// foo\n"+kw[i]+" /* for(int i=0; i<100; i++) {} */\nA \n{\n\t\n};\n\n//bar\n\n", tester.fDoc.get()); //$NON-NLS-1$
}
}
/**
* Tests that brackets are inserted (without semi-colons) in appropriate
* contexts
* @throws BadLocationException
*/
public void testBracketInsertion() throws BadLocationException {
AutoEditTester tester = createAutoEditTester();
tester.type("for (;;) {\n");
assertEquals("for (;;) {\n\t\r\n}", tester.fDoc.get()); //$NON-NLS-1$
tester.reset();
tester.type("for /*class*/ (;;) {\n"); //$NON-NLS-1$
assertEquals("for /*class*/ (;;) {\n\t\r\n}", tester.fDoc.get()); //$NON-NLS-1$
tester.reset();
tester.type("for (;;) /*class*/ {\n"); //$NON-NLS-1$
assertEquals("for (;;) /*class*/ {\n\t\r\n}", tester.fDoc.get()); //$NON-NLS-1$
}
}

View file

@ -684,71 +684,6 @@ public class CHeuristicScannerTest extends TestCase {
Assert.assertEquals(" ", indent); Assert.assertEquals(" ", indent);
} }
public void testAnonymousIndentation1() {
fDocument.set( " MenuItem mi= new MenuItem(\"About...\");\n" +
" mi->addActionListener(\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() { public void testListAlignmentMethodDeclaration() {
// parameter declaration - alignment with parenthesis // parameter declaration - alignment with parenthesis
fDocument.set( "\tvoid proc ( int par1, int par2,\n" + fDocument.set( "\tvoid proc ( int par1, int par2,\n" +
@ -801,25 +736,13 @@ public class CHeuristicScannerTest extends TestCase {
fDocument.set( fDocument.set(
" switch (i) {\n" + " switch (i) {\n" +
" case 1:\n" + " case 1:\n" +
" new Runnable() {\n" + " do {\n" +
""); "");
String indent= fScanner.computeIndentation(fDocument.getLength()).toString(); String indent= fScanner.computeIndentation(fDocument.getLength()).toString();
Assert.assertEquals(" ", indent); 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 { public void testClassInstanceCreationHeuristic() throws Exception {
fDocument.set(" method(new std::vector<std::string>(10), foo, new int[])"); fDocument.set(" method(new std::vector<std::string>(10), foo, new int[])");
@ -841,6 +764,46 @@ public class CHeuristicScannerTest extends TestCase {
assertFalse(fHeuristicScanner.looksLikeClassInstanceCreationBackward(offset, CHeuristicScanner.UNBOUND)); 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 T> class A : public B<int,float>, protected C<T> {");
offset= fDocument.get().indexOf("{");
assertTrue(fHeuristicScanner.looksLikeCompositeTypeDefinitionBackward(offset, CHeuristicScanner.UNBOUND));
}
public void testShiftOperator() throws Exception { public void testShiftOperator() throws Exception {
fDocument.set( fDocument.set(
" for (int j = 0; j == 0; j ++) {\n" + " for (int j = 0; j == 0; j ++) {\n" +

View file

@ -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 * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -303,6 +303,13 @@ public class CAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
if (reference != null) if (reference != null)
buf.append(reference); buf.append(reference);
buf.append('}'); 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 // insert extra line upon new line between two braces
else if (c.offset > start && contentStart < lineEnd && d.getChar(contentStart) == '}') { else if (c.offset > start && contentStart < lineEnd && d.getChar(contentStart) == '}') {

View file

@ -479,6 +479,8 @@ public final class CHeuristicScanner implements Symbols {
return TokenCLASS; return TokenCLASS;
if ("while".equals(s)) //$NON-NLS-1$ if ("while".equals(s)) //$NON-NLS-1$
return TokenWHILE; return TokenWHILE;
if ("union".equals(s)) //$NON-NLS-1$
return TokenUNION;
break; break;
case 6: case 6:
if ("delete".equals(s)) //$NON-NLS-1$ if ("delete".equals(s)) //$NON-NLS-1$
@ -499,6 +501,8 @@ public final class CHeuristicScanner implements Symbols {
return TokenDEFAULT; return TokenDEFAULT;
if ("private".equals(s)) //$NON-NLS-1$ if ("private".equals(s)) //$NON-NLS-1$
return TokenPRIVATE; return TokenPRIVATE;
if ("virtual".equals(s)) //$NON-NLS-1$
return TokenVIRTUAL;
break; break;
case 9: case 9:
if ("protected".equals(s)) //$NON-NLS-1$ if ("protected".equals(s)) //$NON-NLS-1$
@ -867,17 +871,17 @@ public final class CHeuristicScanner implements Symbols {
* <code>true</code> if <code>start</code> is at the following positions (|): * <code>true</code> if <code>start</code> is at the following positions (|):
* *
* <pre> * <pre>
* new new std::vector&lt;std::string&gt;|(10) * new std::vector&lt;std::string&gt;|(10)
* new new str_vector |(10) * new str_vector |(10)
* new new / * comment * / str_vector |(10) * new / * comment * / str_vector |(10)
* </pre> * </pre>
* *
* but not the following: * but not the following:
* *
* <pre> * <pre>
* new new std::vector&lt;std::string&gt;(10)| * new std::vector&lt;std::string&gt;(10)|
* new new std::vector&lt;std::string&gt;|(10) * new std::vector&lt;std::string&gt;|(10)
* new new vector (10)| * new vector (10)|
* vector |(10) * vector |(10)
* </pre> * </pre>
* *
@ -907,8 +911,8 @@ public final class CHeuristicScanner implements Symbols {
/** /**
* Returns <code>true</code> if the document, when scanned backwards from <code>start</code> * Returns <code>true</code> if the document, when scanned backwards from <code>start</code>
* appears to contain a field reference, i.e. a (optional) name preceded by a <code>.</code> or <code>-&gt;</code> * appears to contain a field reference, i.e. a (optional) name preceded by a <code>.</code>
* The <code>start</code> must be before the operator. * or <code>-&gt;</code> or <code>::</code>.
* *
* @param start the position after the field reference operator. * @param start the position after the field reference operator.
* @param bound the first position in <code>fDocument</code> to not consider any more, with * @param bound the first position in <code>fDocument</code> to not consider any more, with
@ -928,7 +932,129 @@ public final class CHeuristicScanner implements Symbols {
if (token == Symbols.TokenMINUS) { if (token == Symbols.TokenMINUS) {
return true; return true;
} }
} else if (token == Symbols.TokenCOLON) {
token= previousToken(getPosition(), bound);
if (token == Symbols.TokenCOLON) {
return true;
}
} }
return false; return false;
} }
/**
* Returns <code>true</code> if the document, when scanned backwards from <code>start</code>
* appears to be a composite type (class, struct, union) or enum definition. Examples:
*
* <pre>
* class A {
* struct A {
* class A : B {
* class A : virtual public B, protected C&lt;T&gt; {
* enum E {
* </pre>
*
* @param start the position of the opening brace.
* @param bound the first position in <code>fDocument</code> to not consider any more, with
* <code>bound</code> &lt; <code>start</code>, or <code>UNBOUND</code>
* @return <code>true</code> 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;
}
}
} }

View file

@ -54,6 +54,8 @@ public interface Symbols {
int TokenDELETE= 1027; int TokenDELETE= 1027;
int TokenCLASS= 1028; int TokenCLASS= 1028;
int TokenSTRUCT= 1029; int TokenSTRUCT= 1029;
int TokenENUM= 1030; int TokenUNION= 1030;
int TokenENUM= 1031;
int TokenVIRTUAL= 1032;
int TokenIDENT= 2000; int TokenIDENT= 2000;
} }