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:
parent
d09020f2e4
commit
92a86b658e
5 changed files with 278 additions and 88 deletions
|
@ -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; 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$
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -684,71 +684,6 @@ public class CHeuristicScannerTest extends TestCase {
|
|||
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() {
|
||||
// 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<std::string>(10), foo, new int[])");
|
||||
|
||||
|
@ -841,6 +764,46 @@ public class CHeuristicScannerTest extends TestCase {
|
|||
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 {
|
||||
fDocument.set(
|
||||
" for (int j = 0; j == 0; j ++) {\n" +
|
||||
|
|
|
@ -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) == '}') {
|
||||
|
|
|
@ -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 {
|
|||
* <code>true</code> if <code>start</code> is at the following positions (|):
|
||||
*
|
||||
* <pre>
|
||||
* 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)
|
||||
* </pre>
|
||||
*
|
||||
* but not the following:
|
||||
*
|
||||
* <pre>
|
||||
* 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)
|
||||
* </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>
|
||||
* appears to contain a field reference, i.e. a (optional) name preceded by a <code>.</code> or <code>-></code>
|
||||
* The <code>start</code> must be before the operator.
|
||||
* appears to contain a field reference, i.e. a (optional) name preceded by a <code>.</code>
|
||||
* or <code>-></code> or <code>::</code>.
|
||||
*
|
||||
* @param start the position after the field reference operator.
|
||||
* @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) {
|
||||
return true;
|
||||
}
|
||||
} else if (token == Symbols.TokenCOLON) {
|
||||
token= previousToken(getPosition(), bound);
|
||||
if (token == Symbols.TokenCOLON) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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<T> {
|
||||
* 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> < <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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue