1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-12 10:45:37 +02:00

Bug 435235 - Automatically close opening brace of list initializer

This commit is contained in:
Sergey Prigogin 2014-05-19 18:52:07 -07:00
parent c91dfd42d0
commit 5b6571f5d9
2 changed files with 84 additions and 15 deletions

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation and others. * Copyright (c) 2000, 2014 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
@ -8,6 +8,7 @@
* Contributors: * Contributors:
* IBM Corporation - initial API and implementation * IBM Corporation - initial API and implementation
* Anton Leherbauer (Wind River Systems) * Anton Leherbauer (Wind River Systems)
* Sergey Prigogin (Google)
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.ui.tests.text; package org.eclipse.cdt.ui.tests.text;
@ -77,7 +78,7 @@ public class BracketInserterTest extends TestCase {
// Document offsets. // Document offsets.
private static final int INCLUDE_OFFSET= 9; private static final int INCLUDE_OFFSET= 9;
private static final int BODY_OFFSET= 212; private static final int BODY_OFFSET= 213;
private static final int ARGS_OFFSET= 184; private static final int ARGS_OFFSET= 184;
private static final int BRACKETS_OFFSET= 262; private static final int BRACKETS_OFFSET= 262;
@ -123,7 +124,7 @@ public class BracketInserterTest extends TestCase {
IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(path); IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(path);
assertTrue(file != null && file.exists()); assertTrue(file != null && file.exists());
try { try {
return (CEditor)EditorTestHelper.openInEditor(file, true); return (CEditor) EditorTestHelper.openInEditor(file, true);
} catch (PartInitException e) { } catch (PartInitException e) {
fail(); fail();
return null; return null;
@ -175,19 +176,19 @@ public class BracketInserterTest extends TestCase {
setCaret(BODY_OFFSET); setCaret(BODY_OFFSET);
type("(((("); type("((((");
// delete two levels // Delete two levels.
linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION); linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION);
linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION); linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION);
assertEquals("(())", fDocument.get(BODY_OFFSET, 4)); assertEquals("(())", fDocument.get(BODY_OFFSET, 4));
assertEquals(BODY_OFFSET + 2, getCaret()); assertEquals(BODY_OFFSET + 2, getCaret());
// delete the second-last level // Delete the second-last level
linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION); linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION);
assertEquals("()", fDocument.get(BODY_OFFSET, 2)); assertEquals("()", fDocument.get(BODY_OFFSET, 2));
assertEquals(BODY_OFFSET + 1, getCaret()); assertEquals(BODY_OFFSET + 1, getCaret());
// delete last level // Delete last level
linkedType(SWT.BS, false, ILinkedModeListener.EXTERNAL_MODIFICATION); linkedType(SWT.BS, false, ILinkedModeListener.EXTERNAL_MODIFICATION);
assertEquals(TU_CONTENTS, fDocument.get()); assertEquals(TU_CONTENTS, fDocument.get());
assertEquals(BODY_OFFSET, getCaret()); assertEquals(BODY_OFFSET, getCaret());
@ -304,18 +305,18 @@ public class BracketInserterTest extends TestCase {
setCaret(BODY_OFFSET); setCaret(BODY_OFFSET);
type("#define MACRO "); type("#define MACRO ");
int offset = getCaret(); int offset = getCaret();
// enter opening quote (should be closed again) // Enter opening quote (should be closed again).
type('"'); type('"');
assertEquals("\"\"", fDocument.get(offset, 2)); assertEquals("\"\"", fDocument.get(offset, 2));
assertSingleLinkedPosition(offset + 1); assertSingleLinkedPosition(offset + 1);
// enter closing quote (should not add a quote, but proceed cursor) // Enter closing quote (should not add a quote, but proceed cursor).
type('"'); type('"');
assertEquals("\"\"", fDocument.get(offset, 2)); assertEquals("\"\"", fDocument.get(offset, 2));
assertEquals(offset + 2, getCaret()); assertEquals(offset + 2, getCaret());
// delete closing quote and enter quote again // Delete closing quote and enter quote again.
type(SWT.BS); type(SWT.BS);
assertEquals("\"", fDocument.get(offset, 1)); assertEquals("\"", fDocument.get(offset, 1));
int length = fDocument.getLength(); int length = fDocument.getLength();
@ -358,6 +359,23 @@ public class BracketInserterTest extends TestCase {
assertSingleLinkedPosition(BODY_OFFSET + 5); assertSingleLinkedPosition(BODY_OFFSET + 5);
} }
public void testCurlyBraceInsideParentheses() throws Exception {
setCaret(BODY_OFFSET);
type("f({");
assertEquals("f({})", fDocument.get(BODY_OFFSET, 5));
assertSingleLinkedPosition(BODY_OFFSET + 3, true);
}
public void testCurlyBraceOutsideParentheses() throws Exception {
setCaret(BODY_OFFSET);
type("struct A {");
assertFalse("}".equals(fDocument.get(BODY_OFFSET + 10, 1)));
assertEquals(BODY_OFFSET + 10, getCaret());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testAngleBracketsInInclude() throws Exception { public void testAngleBracketsInInclude() throws Exception {
setCaret(INCLUDE_OFFSET); setCaret(INCLUDE_OFFSET);
type('<'); type('<');
@ -396,9 +414,13 @@ public class BracketInserterTest extends TestCase {
/* utilities */ /* utilities */
private void assertSingleLinkedPosition(int offset) { private void assertSingleLinkedPosition(int offset) {
assertSingleLinkedPosition(offset, false);
}
private void assertSingleLinkedPosition(int offset, boolean nested) {
assertEquals(offset, getCaret()); assertEquals(offset, getCaret());
LinkedPosition position= assertModel(false).findPosition(new LinkedPosition(fDocument, offset, 0)); LinkedPosition position= assertModel(nested).findPosition(new LinkedPosition(fDocument, offset, 0));
assertNotNull(position); assertNotNull(position);
assertEquals(offset, position.getOffset()); assertEquals(offset, position.getOffset());
assertEquals(0, position.getLength()); assertEquals(0, position.getLength());

View file

@ -535,6 +535,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
private boolean fCloseBrackets = true; private boolean fCloseBrackets = true;
private boolean fCloseStrings = true; private boolean fCloseStrings = true;
private boolean fCloseAngularBrackets = true; private boolean fCloseAngularBrackets = true;
private boolean fCloseBraces = true;
private final String CATEGORY = toString(); private final String CATEGORY = toString();
private IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY); private IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY);
private Deque<BracketLevel> fBracketLevelStack = new ArrayDeque<>(); private Deque<BracketLevel> fBracketLevelStack = new ArrayDeque<>();
@ -551,6 +552,10 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
fCloseAngularBrackets = enabled; fCloseAngularBrackets = enabled;
} }
public void setCloseBracesEnabled(boolean enabled) {
fCloseBraces = enabled;
}
private boolean isAngularIntroducer(String identifier) { private boolean isAngularIntroducer(String identifier) {
return identifier.length() > 0 && (Character.isUpperCase(identifier.charAt(0)) return identifier.length() > 0 && (Character.isUpperCase(identifier.charAt(0))
|| angularIntroducers.contains(identifier) || angularIntroducers.contains(identifier)
@ -560,13 +565,14 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
@Override @Override
public void verifyKey(VerifyEvent event) { public void verifyKey(VerifyEvent event) {
// Early pruning to slow down normal typing as little as possible. // Early pruning to minimize overhead for normal typing.
if (!event.doit || getInsertMode() != SMART_INSERT) if (!event.doit || getInsertMode() != SMART_INSERT)
return; return;
switch (event.character) { switch (event.character) {
case '(': case '(':
case '<': case '<':
case '[': case '[':
case '{':
case '\'': case '\'':
case '\"': case '\"':
break; break;
@ -624,6 +630,16 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
return; return;
break; break;
case '{':
// An opening brace inside parentheses probably starts an initializer list -
// close it.
if (!fCloseBraces
|| nextToken == Symbols.TokenIDENT
|| next != null && next.length() > 1
|| !isInsideParentheses(scanner, offset - 1))
return;
break;
case '\'': case '\'':
case '"': case '"':
if (!fCloseStrings if (!fCloseStrings
@ -687,6 +703,24 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
} }
} }
private boolean isInsideParentheses(CHeuristicScanner scanner, int offset) {
int depth = 0;
// Limit the scanning distance to 100 tokens.
for (int i = 0; i < 100; i++) {
int token = scanner.previousToken(offset, 0);
if (token == Symbols.TokenLPAREN) {
if (--depth < 0)
return true;
} else if (token == Symbols.TokenRPAREN) {
++depth;
} else if (token == Symbols.TokenEOF) {
return false;
}
offset = scanner.getPosition();
}
return false;
}
private boolean isInsideStringInPreprocessorDirective(ITypedRegion partition, IDocument document, int offset) throws BadLocationException { private boolean isInsideStringInPreprocessorDirective(ITypedRegion partition, IDocument document, int offset) throws BadLocationException {
if (ICPartitions.C_PREPROCESSOR.equals(partition.getType()) && offset < document.getLength()) { if (ICPartitions.C_PREPROCESSOR.equals(partition.getType()) && offset < document.getLength()) {
// Use temporary document to test whether offset is inside non-default partition. // Use temporary document to test whether offset is inside non-default partition.
@ -706,7 +740,6 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
@Override @Override
public void left(LinkedModeModel environment, int flags) { public void left(LinkedModeModel environment, int flags) {
final BracketLevel level = fBracketLevelStack.pop(); final BracketLevel level = fBracketLevelStack.pop();
if (flags != ILinkedModeListener.EXTERNAL_MODIFICATION) if (flags != ILinkedModeListener.EXTERNAL_MODIFICATION)
@ -1197,6 +1230,8 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
private static final String CLOSE_BRACKETS = PreferenceConstants.EDITOR_CLOSE_BRACKETS; private static final String CLOSE_BRACKETS = PreferenceConstants.EDITOR_CLOSE_BRACKETS;
/** Preference key for automatically closing angular brackets */ /** Preference key for automatically closing angular brackets */
private static final String CLOSE_ANGULAR_BRACKETS = PreferenceConstants.EDITOR_CLOSE_ANGULAR_BRACKETS; private static final String CLOSE_ANGULAR_BRACKETS = PreferenceConstants.EDITOR_CLOSE_ANGULAR_BRACKETS;
/** Preference key for automatically closing curly braces */
private static final String CLOSE_BRACES = PreferenceConstants.EDITOR_CLOSE_BRACES;
/** Preference key for compiler task tags */ /** Preference key for compiler task tags */
private static final String TODO_TASK_TAGS = CCorePreferenceConstants.TODO_TASK_TAGS; private static final String TODO_TASK_TAGS = CCorePreferenceConstants.TODO_TASK_TAGS;
@ -1532,6 +1567,11 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
return; return;
} }
if (CLOSE_BRACES.equals(property)) {
fBracketInserter.setCloseBracesEnabled(newBooleanValue);
return;
}
if (CLOSE_STRINGS.equals(property)) { if (CLOSE_STRINGS.equals(property)) {
fBracketInserter.setCloseStringsEnabled(newBooleanValue); fBracketInserter.setCloseStringsEnabled(newBooleanValue);
return; return;
@ -2403,11 +2443,13 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
IPreferenceStore preferenceStore = getPreferenceStore(); IPreferenceStore preferenceStore = getPreferenceStore();
boolean closeBrackets = preferenceStore.getBoolean(CLOSE_BRACKETS); boolean closeBrackets = preferenceStore.getBoolean(CLOSE_BRACKETS);
boolean closeAngularBrackets = preferenceStore.getBoolean(CLOSE_ANGULAR_BRACKETS); boolean closeAngularBrackets = preferenceStore.getBoolean(CLOSE_ANGULAR_BRACKETS);
boolean closeBraces = preferenceStore.getBoolean(CLOSE_BRACES);
boolean closeStrings = preferenceStore.getBoolean(CLOSE_STRINGS); boolean closeStrings = preferenceStore.getBoolean(CLOSE_STRINGS);
fBracketInserter.setCloseBracketsEnabled(closeBrackets); fBracketInserter.setCloseBracketsEnabled(closeBrackets);
fBracketInserter.setCloseStringsEnabled(closeStrings);
fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets); fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets);
fBracketInserter.setCloseAngularBracketsEnabled(closeBraces);
fBracketInserter.setCloseStringsEnabled(closeStrings);
ISourceViewer sourceViewer = getSourceViewer(); ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension) if (sourceViewer instanceof ITextViewerExtension)
@ -2867,8 +2909,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
return false; return false;
try { try {
return isBracket(document.getChar(offset - 1)) && return isBracket(document.getChar(offset - 1)) && isBracket(document.getChar(offset));
isBracket(document.getChar(offset));
} catch (BadLocationException e) { } catch (BadLocationException e) {
return false; return false;
} }
@ -2904,6 +2945,12 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
case ']': case ']':
return '['; return '[';
case '{':
return '}';
case '}':
return '{';
case '"': case '"':
return character; return character;