diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java index f766c10d1c0..a60e5a07ceb 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java @@ -92,6 +92,7 @@ import org.eclipse.cdt.core.dom.ast.c.ICASTEnumerationSpecifier; import org.eclipse.cdt.core.dom.ast.c.ICASTPointer; import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCastExpression; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer; @@ -112,6 +113,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateSpecialization; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplatedTypeTemplateParameter; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTryBlockStatement; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisiblityLabel; @@ -202,9 +204,11 @@ public class CodeFormatterVisitor extends CPPASTVisitor { private String fTranslationUnitFile; private boolean fInsideFor; + private boolean fExpectSemicolonAfterDeclaration= true; private MultiStatus fStatus; + public CodeFormatterVisitor(DefaultCodeFormatterOptions preferences, Map settings, int offset, int length) { localScanner = new Scanner() { public Token nextToken() { @@ -465,9 +469,7 @@ public class CodeFormatterVisitor extends CPPASTVisitor { } if (node instanceof ICPPASTFunctionTryBlockDeclarator) { - visit((IASTStandardFunctionDeclarator)node); - skipNode(node); - return PROCESS_SKIP; + visit((ICPPASTFunctionTryBlockDeclarator)node); } else if (node instanceof ICPPASTFunctionDeclarator) { return visit((ICPPASTFunctionDeclarator)node); } else if (node instanceof IASTStandardFunctionDeclarator) { @@ -586,6 +588,10 @@ public class CodeFormatterVisitor extends CPPASTVisitor { } else if (node instanceof ICPPASTWhileStatement) { // TLETODO [formatter] handle C++ specifics visit((IASTWhileStatement)node); + } else if (node instanceof ICPPASTCatchHandler) { + visit((ICPPASTCatchHandler)node); + } else if (node instanceof ICPPASTTryBlockStatement) { + visit((ICPPASTTryBlockStatement)node); } else if (node instanceof IASTWhileStatement) { visit((IASTWhileStatement)node); } else if (node instanceof IASTDoStatement) { @@ -858,6 +864,16 @@ public class CodeFormatterVisitor extends CPPASTVisitor { } scribe.printTrailingComment(); scribe.startNewLine(); + + // hack: catch handlers are part of declarator + if (decl instanceof ICPPASTFunctionTryBlockDeclarator) { + ICPPASTCatchHandler[] catchHandlers= ((ICPPASTFunctionTryBlockDeclarator)decl).getCatchHandlers(); + for (int i= 0; i < catchHandlers.length; i++) { + catchHandlers[i].accept(this); + scribe.printTrailingComment(); + scribe.startNewLine(); + } + } return PROCESS_SKIP; } @@ -878,6 +894,13 @@ public class CodeFormatterVisitor extends CPPASTVisitor { } } + if (node instanceof ICPPASTFunctionTryBlockDeclarator) { + scribe.startNewLine(); + scribe.printNextToken(Token.t_try, false); + scribe.printTrailingComment(); + // for catch handlers @see #visit(IASTFunctionDefinition) + } + final ICPPASTConstructorChainInitializer[] constructorChain= node.getConstructorChain(); if (constructorChain != null && constructorChain.length > 0) { // TLETODO [formatter] need special constructor chain alignment @@ -1044,11 +1067,13 @@ public class CodeFormatterVisitor extends CPPASTVisitor { final ListAlignment align= new ListAlignment(Alignment.M_COMPACT_SPLIT); formatList(declarators, align, false, false); } - if (peekNextToken() != Token.tSEMI) { - scribe.skipToToken(Token.tSEMI); + if (fExpectSemicolonAfterDeclaration) { + if (peekNextToken() != Token.tSEMI) { + scribe.skipToToken(Token.tSEMI); + } + scribe.printNextToken(Token.tSEMI, preferences.insert_space_before_semicolon); + scribe.printTrailingComment(); } - scribe.printNextToken(Token.tSEMI, preferences.insert_space_before_semicolon); - scribe.printTrailingComment(); return PROCESS_SKIP; } @@ -1355,6 +1380,49 @@ public class CodeFormatterVisitor extends CPPASTVisitor { } } + private int visit(ICPPASTTryBlockStatement node) { + scribe.printNextToken(Token.t_try, scribe.printComment()); + final IASTStatement tryBody= node.getTryBody(); + if (tryBody != null) { + tryBody.accept(this); + } + scribe.printTrailingComment(); + ICPPASTCatchHandler[] catchHandlers= node.getCatchHandlers(); + for (int i= 0; i < catchHandlers.length; i++) { + catchHandlers[i].accept(this); + scribe.printTrailingComment(); + } + return PROCESS_SKIP; + } + + private int visit(ICPPASTCatchHandler node) { + scribe.printNextToken(Token.t_catch, true); + scribe.printNextToken(Token.tLPAREN, /* preferences.insert_space_before_opening_paren_in_catch */ true); + if (/* preferences.insert_space_after_opening_paren_in_catch */ false) { + scribe.space(); + } + final IASTDeclaration decl= node.getDeclaration(); + if (decl != null) { + fExpectSemicolonAfterDeclaration= false; + try { + decl.accept(this); + } finally { + fExpectSemicolonAfterDeclaration= true; + } + } else if (node.isCatchAll()) { + scribe.printNextToken(Token.tELIPSE); + } + scribe.printNextToken(Token.tRPAREN, /* preferences.insert_space_before_closing_paren_in_catch */ false); + if (/* preferences.insert_space_after_closing_paren_in_catch */ true) { + scribe.space(); + } + final IASTStatement catchBody= node.getCatchBody(); + if (catchBody != null) { + catchBody.accept(this); + } + return PROCESS_SKIP; + } + private int visit(IASTCompoundStatement node) { formatBlock(node, preferences.brace_position_for_block, preferences.insert_space_before_opening_brace_in_block, preferences.indent_statements_compare_to_block); return PROCESS_SKIP; diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java index d1513f43726..62ec04ce2ff 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java @@ -285,4 +285,58 @@ public class CodeFormatterTest extends BaseUITestCase { assertFormatterResult(); } + //main + //( + // int argc, + // char const * argv[] + //) + //try + //{ + // for ( int i = 1 ; i < argc ; ++i ) + // { + // } + // return 0; + //} + //catch ( float e ) + //{ + // return 1; + //} + //catch ( ... ) + //{ + // return 2; + //} + + //main(int argc, char const * argv[]) + //try { + // for (int i = 1; i < argc; ++i) { + // } + // return 0; + //} + //catch (float e) { + // return 1; + //} + //catch (...) { + // return 2; + //} + public void testFunctionTryCatchBlock() throws Exception { + assertFormatterResult(); + } + + //main(int argc, char const * argv[]) { try { for (int i = 1; i < argc; ++i) { } return 0; } catch (float e) { return 1; } catch (...) { return 2; } } + + //main(int argc, char const * argv[]) { + // try { + // for (int i = 1; i < argc; ++i) { + // } + // return 0; + // } catch (float e) { + // return 1; + // } catch (...) { + // return 2; + // } + //} + public void testTryCatchBlock() throws Exception { + assertFormatterResult(); + } + } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingReconciler.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingReconciler.java index cf2d15cba7b..c8bc84828ce 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingReconciler.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingReconciler.java @@ -33,17 +33,26 @@ import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTExpression; -import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTImageLocation; +import org.eclipse.cdt.core.dom.ast.IASTInitializer; import org.eclipse.cdt.core.dom.ast.IASTMacroExpansion; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; +import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition; +import org.eclipse.cdt.core.dom.ast.IASTProblem; import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IASTTypeId; +import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator; import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionTryBlockDeclarator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ILanguage; import org.eclipse.cdt.ui.CUIPlugin; @@ -63,10 +72,92 @@ import org.eclipse.cdt.internal.ui.text.ICReconcilingListener; */ public class SemanticHighlightingReconciler implements ICReconcilingListener { + /** + * AST visitor to test whether a node is a leaf node. + */ + public static final class LeafNodeTester extends CPPASTVisitor { + { + shouldVisitNames= true; + shouldVisitDeclarations= true; + shouldVisitInitializers= true; + shouldVisitParameterDeclarations= true; + shouldVisitDeclarators= true; + shouldVisitDeclSpecifiers= true; + shouldVisitExpressions= true; + shouldVisitStatements= true; + shouldVisitTypeIds= true; + shouldVisitEnumerators= true; + shouldVisitTranslationUnit= false; + shouldVisitProblems= true; + shouldVisitComments= false; + shouldVisitBaseSpecifiers= true; + shouldVisitNamespaces= true; + shouldVisitTemplateParameters= true; + } + private int fVisits; + + private int processNode(IASTNode node) { + if (++fVisits > 1) + return PROCESS_ABORT; + return PROCESS_CONTINUE; + } + public int visit(ICPPASTBaseSpecifier specifier) { + return processNode(specifier); + } + public int visit(ICPPASTNamespaceDefinition namespace) { + return processNode(namespace); + } + public int visit(ICPPASTTemplateParameter parameter) { + return processNode(parameter); + } + public int visit(IASTDeclaration declaration) { + return processNode(declaration); + } + public int visit(IASTDeclarator declarator) { + return processNode(declarator); + } + public int visit(IASTDeclSpecifier declSpec) { + return processNode(declSpec); + } + public int visit(IASTEnumerator enumerator) { + return processNode(enumerator); + } + public int visit(IASTExpression expression) { + return processNode(expression); + } + public int visit(IASTInitializer initializer) { + return processNode(initializer); + } + public int visit(IASTName name) { + return processNode(name); + } + public int visit(IASTParameterDeclaration parameterDeclaration) { + return processNode(parameterDeclaration); + } + public int visit(IASTProblem problem) { + return processNode(problem); + } + public int visit(IASTStatement statement) { + return processNode(statement); + } + public int visit(IASTTranslationUnit tu) { + return processNode(tu); + } + public int visit(IASTTypeId typeId) { + return processNode(typeId); + } + public boolean isLeafNode(IASTNode node) { + fVisits= 0; + node.accept(this); + return fVisits <= 1; + } + } + /** * Collects positions from the AST. */ private class PositionCollector extends CPPASTVisitor { + { shouldVisitTranslationUnit= true; shouldVisitNames= true; @@ -77,11 +168,13 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener { shouldVisitDeclarators= true; shouldVisitNamespaces= true; } + private boolean shouldVisitCatchHandlers= true; /** The semantic token */ private SemanticToken fToken= new SemanticToken(); private String fFilePath; private int fMinLocation; + private final LeafNodeTester fgLeafNodeTester= new LeafNodeTester(); /** * @param filePath @@ -100,7 +193,7 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener { for (int i= 0; i < macroDefs.length; i++) { IASTPreprocessorMacroDefinition macroDef= macroDefs[i]; if (fFilePath.equals(macroDef.getContainingFilename())) { - visit(macroDef.getName()); + visitNode(macroDef.getName()); } } // TODO visit macro expansions @@ -127,6 +220,22 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener { return PROCESS_CONTINUE; } + /* + * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#leave(org.eclipse.cdt.core.dom.ast.IASTDeclaration) + */ + public int leave(IASTDeclaration declaration) { + if (!shouldVisitCatchHandlers && declaration instanceof IASTFunctionDefinition) { + shouldVisitCatchHandlers= true; + IASTFunctionDefinition functionDef= (IASTFunctionDefinition) declaration; + ICPPASTFunctionTryBlockDeclarator declarator= (ICPPASTFunctionTryBlockDeclarator) functionDef.getDeclarator(); + ICPPASTCatchHandler[] catchHandlers= declarator.getCatchHandlers(); + for (int i = 0; i < catchHandlers.length; i++) { + catchHandlers[i].accept(this); + } + } + return PROCESS_CONTINUE; + } + /* * @see org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor#visit(org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition) */ @@ -157,9 +266,12 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener { if (checkForMacro(declarator)) { return PROCESS_SKIP; } + if (declarator instanceof ICPPASTFunctionTryBlockDeclarator) { + shouldVisitCatchHandlers= false; + } return PROCESS_CONTINUE; } - + /* * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTExpression) */ @@ -174,6 +286,9 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener { * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTStatement) */ public int visit(IASTStatement statement) { + if (!shouldVisitCatchHandlers && statement instanceof ICPPASTCatchHandler) { + return PROCESS_SKIP; + } if (checkForMacro(statement)) { return PROCESS_SKIP; } @@ -194,35 +309,42 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener { } private boolean checkForMacro(IASTNode node) { + boolean isLeafNode= isLeafNode(node); IASTNodeLocation[] nodeLocations= node.getNodeLocations(); - if (nodeLocations.length == 1 && nodeLocations[0] instanceof IASTMacroExpansion) { - IASTNodeLocation useLocation= getMinFileLocation(nodeLocations); - if (useLocation != null) { - final int useOffset = useLocation.getNodeOffset(); - if (useOffset <= fMinLocation) { - // performance: we had that macro expansion already - return false; - } - fMinLocation= useOffset; - // TLETODO This does not work correctly for nested macro substitutions - IASTPreprocessorMacroDefinition macroDef= ((IASTMacroExpansion)nodeLocations[0]).getMacroDefinition(); - final int macroLength; - IASTNodeLocation defLocation= macroDef.getName().getFileLocation(); - if (defLocation != null) { - macroLength= defLocation.getNodeLength(); - } else { - macroLength= macroDef.getName().toCharArray().length; - } - IASTNode macroNode= node.getTranslationUnit().selectNodeForLocation(fFilePath, useOffset, macroLength); - if (macroNode != null && visitMacro(macroNode, macroLength)) { - fMinLocation= useOffset + macroLength; - return false; + for (int i= 0; i < nodeLocations.length; i++) { + IASTNodeLocation nodeLocation= nodeLocations[i]; + if (nodeLocation instanceof IASTMacroExpansion) { + IASTNodeLocation useLocation= nodeLocation.asFileLocation(); + if (useLocation != null) { + final int useOffset = useLocation.getNodeOffset(); + if (useOffset > fMinLocation) { + fMinLocation= useOffset; + IASTPreprocessorMacroDefinition macroDef= ((IASTMacroExpansion)nodeLocation).getMacroDefinition(); + final int macroLength; + IASTNodeLocation defLocation= macroDef.getName().getFileLocation(); + if (defLocation != null) { + macroLength= defLocation.getNodeLength(); + } else { + macroLength= macroDef.getName().toCharArray().length; + } + IASTNode macroNode= node.getTranslationUnit().selectNodeForLocation(fFilePath, useOffset, macroLength); + if (macroNode != null && visitMacro(macroNode, macroLength)) { + fMinLocation= useOffset + macroLength; + } + } } } + if (!isLeafNode) { + break; + } } return false; } + private boolean isLeafNode(IASTNode node) { + return fgLeafNodeTester.isLeafNode(node); + } + private boolean visitMacro(IASTNode node, int macroLength) { fToken.update(node); for (int i= 0, n= fJobSemanticHighlightings.length; i < n; ++i) { @@ -318,14 +440,6 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener { } } - private IASTFileLocation getMinFileLocation(IASTNodeLocation[] locations) { - if (locations == null || locations.length == 0) { - return null; - } - final IASTNodeLocation nodeLocation= locations[0]; - return nodeLocation.asFileLocation(); - } - /** * Add a position with the given range and highlighting iff it does not exist already. * diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightings.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightings.java index 0b13e27b18e..63a7f2f27eb 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightings.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightings.java @@ -1640,6 +1640,9 @@ public class SemanticHighlightings { if (node instanceof IASTProblem) { return true; } + if (node instanceof ICPPASTQualifiedName || node instanceof ICPPASTTemplateId) { + return false; + } IBinding binding= token.getBinding(); if (binding instanceof IProblemBinding) { return true;