From 5b6571f5d9e52cc7005ec4950958d6fc8c4cd391 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Mon, 19 May 2014 18:52:07 -0700 Subject: [PATCH] Bug 435235 - Automatically close opening brace of list initializer --- .../ui/tests/text/BracketInserterTest.java | 42 ++++++++++---- .../cdt/internal/ui/editor/CEditor.java | 57 +++++++++++++++++-- 2 files changed, 84 insertions(+), 15 deletions(-) diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/BracketInserterTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/BracketInserterTest.java index f52d394efc5..a255c58552d 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/BracketInserterTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/BracketInserterTest.java @@ -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 * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Anton Leherbauer (Wind River Systems) + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.ui.tests.text; @@ -77,7 +78,7 @@ public class BracketInserterTest extends TestCase { // Document offsets. 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 BRACKETS_OFFSET= 262; @@ -123,7 +124,7 @@ public class BracketInserterTest extends TestCase { IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(path); assertTrue(file != null && file.exists()); try { - return (CEditor)EditorTestHelper.openInEditor(file, true); + return (CEditor) EditorTestHelper.openInEditor(file, true); } catch (PartInitException e) { fail(); return null; @@ -175,19 +176,19 @@ public class BracketInserterTest extends TestCase { setCaret(BODY_OFFSET); type("(((("); - // delete two levels + // Delete two levels. linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION); linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION); assertEquals("(())", fDocument.get(BODY_OFFSET, 4)); assertEquals(BODY_OFFSET + 2, getCaret()); - // delete the second-last level + // Delete the second-last level linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION); assertEquals("()", fDocument.get(BODY_OFFSET, 2)); assertEquals(BODY_OFFSET + 1, getCaret()); - // delete last level + // Delete last level linkedType(SWT.BS, false, ILinkedModeListener.EXTERNAL_MODIFICATION); assertEquals(TU_CONTENTS, fDocument.get()); assertEquals(BODY_OFFSET, getCaret()); @@ -304,18 +305,18 @@ public class BracketInserterTest extends TestCase { setCaret(BODY_OFFSET); type("#define MACRO "); int offset = getCaret(); - // enter opening quote (should be closed again) + // Enter opening quote (should be closed again). type('"'); assertEquals("\"\"", fDocument.get(offset, 2)); 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('"'); assertEquals("\"\"", fDocument.get(offset, 2)); assertEquals(offset + 2, getCaret()); - // delete closing quote and enter quote again + // Delete closing quote and enter quote again. type(SWT.BS); assertEquals("\"", fDocument.get(offset, 1)); int length = fDocument.getLength(); @@ -358,6 +359,23 @@ public class BracketInserterTest extends TestCase { 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 { setCaret(INCLUDE_OFFSET); type('<'); @@ -396,9 +414,13 @@ public class BracketInserterTest extends TestCase { /* utilities */ private void assertSingleLinkedPosition(int offset) { + assertSingleLinkedPosition(offset, false); + } + + private void assertSingleLinkedPosition(int offset, boolean nested) { 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); assertEquals(offset, position.getOffset()); assertEquals(0, position.getLength()); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java index 9ba249fe3e5..f1c134b171f 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java @@ -535,6 +535,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi private boolean fCloseBrackets = true; private boolean fCloseStrings = true; private boolean fCloseAngularBrackets = true; + private boolean fCloseBraces = true; private final String CATEGORY = toString(); private IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY); private Deque fBracketLevelStack = new ArrayDeque<>(); @@ -551,6 +552,10 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi fCloseAngularBrackets = enabled; } + public void setCloseBracesEnabled(boolean enabled) { + fCloseBraces = enabled; + } + private boolean isAngularIntroducer(String identifier) { return identifier.length() > 0 && (Character.isUpperCase(identifier.charAt(0)) || angularIntroducers.contains(identifier) @@ -560,13 +565,14 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi @Override 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) return; switch (event.character) { case '(': case '<': case '[': + case '{': case '\'': case '\"': break; @@ -624,6 +630,16 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi return; 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 '"': 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 { if (ICPartitions.C_PREPROCESSOR.equals(partition.getType()) && offset < document.getLength()) { // 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 public void left(LinkedModeModel environment, int flags) { - final BracketLevel level = fBracketLevelStack.pop(); 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; /** Preference key for automatically closing 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 */ private static final String TODO_TASK_TAGS = CCorePreferenceConstants.TODO_TASK_TAGS; @@ -1532,6 +1567,11 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi return; } + if (CLOSE_BRACES.equals(property)) { + fBracketInserter.setCloseBracesEnabled(newBooleanValue); + return; + } + if (CLOSE_STRINGS.equals(property)) { fBracketInserter.setCloseStringsEnabled(newBooleanValue); return; @@ -2403,11 +2443,13 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi IPreferenceStore preferenceStore = getPreferenceStore(); boolean closeBrackets = preferenceStore.getBoolean(CLOSE_BRACKETS); boolean closeAngularBrackets = preferenceStore.getBoolean(CLOSE_ANGULAR_BRACKETS); + boolean closeBraces = preferenceStore.getBoolean(CLOSE_BRACES); boolean closeStrings = preferenceStore.getBoolean(CLOSE_STRINGS); fBracketInserter.setCloseBracketsEnabled(closeBrackets); - fBracketInserter.setCloseStringsEnabled(closeStrings); fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets); + fBracketInserter.setCloseAngularBracketsEnabled(closeBraces); + fBracketInserter.setCloseStringsEnabled(closeStrings); ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ITextViewerExtension) @@ -2867,8 +2909,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi return false; try { - return isBracket(document.getChar(offset - 1)) && - isBracket(document.getChar(offset)); + return isBracket(document.getChar(offset - 1)) && isBracket(document.getChar(offset)); } catch (BadLocationException e) { return false; } @@ -2904,6 +2945,12 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi case ']': return '['; + case '{': + return '}'; + + case '}': + return '{'; + case '"': return character;