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 5097e70d2a9..a4fcec0abaf 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 @@ -118,6 +118,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamedTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceAlias; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTPointerToMember; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement; @@ -167,8 +168,8 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, } } - private static class ListAlignment { - public int fMode; + private static class ListOptions { + public final int fMode; public boolean fSpaceBeforeComma; public boolean fSpaceAfterComma= true; public boolean fSpaceAfterOpeningParen; @@ -178,11 +179,123 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, public int fContinuationIndentation= -1; public int fTieBreakRule = Alignment.R_INNERMOST; - public ListAlignment(int mode) { + public ListOptions(int mode) { fMode= mode; } } + /* + * Formats a given token at a given position. + * @see #formatList(List, ListAlignment, boolean, boolean, Runnable) + */ + class TrailingTokenFormatter implements Runnable { + private final int tokenType; + private final int tokenPosition; + private final boolean spaceBeforeToken; + private final boolean spaceAfterToken; + + TrailingTokenFormatter(int tokenType, int tokenPosition, + boolean spaceBeforeToken, boolean spaceAfterToken) { + this.tokenType = tokenType; + this.tokenPosition = tokenPosition; + this.spaceBeforeToken = spaceBeforeToken; + this.spaceAfterToken = spaceAfterToken; + } + + public void run() { + int offset = scribe.scanner.getCurrentPosition(); + if (tokenPosition < 0 || offset > tokenPosition) + return; + if (offset < tokenPosition) + scribe.restartAtOffset(tokenPosition); + int token= peekNextToken(); + if (token == tokenType) { + scribe.pendingSpace = false; + scribe.printNextToken(tokenType, spaceBeforeToken); + scribe.printTrailingComment(); + if (spaceAfterToken) { + scribe.space(); + } + } + } + } + + /* + * Formats a trailing comma. + * @see #formatList(List, ListAlignment, boolean, boolean, Runnable) + */ + class TrailingCommaFormatter extends TrailingTokenFormatter { + TrailingCommaFormatter(boolean spaceBeforeComma, boolean spaceAfterComma) { + super(Token.tCOMMA, scribe.findToken(Token.tCOMMA), spaceBeforeComma, spaceAfterComma); + } + } + + /* + * Formats a trailing semicolon. + * @see #formatList(List, ListAlignment, boolean, boolean, Runnable) + */ + class TrailingSemicolonFormatter extends TrailingTokenFormatter { + TrailingSemicolonFormatter(IASTNode node) { + super(Token.tSEMI, getLastNodeCharacterPosition(node), + fInsideFor ? preferences.insert_space_before_semicolon_in_for : + preferences.insert_space_before_semicolon, + false); + } + } + + /* + * Formats the part of a function declaration following the parameter list. + * @see #formatList(List, ListAlignment, boolean, boolean, Runnable) + */ + public class CPPFunctionDeclaratorTailFormatter implements Runnable { + private final ICPPASTFunctionDeclarator node; + private final Runnable continuationFormatter; + + public CPPFunctionDeclaratorTailFormatter(ICPPASTFunctionDeclarator node, + Runnable tailFormatter) { + this.node = node; + this.continuationFormatter = tailFormatter; + } + + public void run() { + boolean needSpace = skipConstVolatileRestrict(); + if (node.getExceptionSpecification() != null && peekNextToken() == Token.t_throw) + return; + // Skip the rest (=0) + if (needSpace && scribe.printComment()) { + scribe.space(); + } + skipNode(node); + if (continuationFormatter != null) + continuationFormatter.run(); + } + } + + public class ClosingParensesisTailFormatter implements Runnable { + private final boolean spaceBeforeClosingParen; + private final Runnable continuationFormatter; + private final int parenPosition; + + public ClosingParensesisTailFormatter(boolean spaceBeforeClosingParen, + Runnable tailFormatter) { + this.spaceBeforeClosingParen = spaceBeforeClosingParen; + this.continuationFormatter = tailFormatter; + this.parenPosition = scribe.findToken(Token.tRPAREN); + } + + public void run() { + int offset = scribe.scanner.getCurrentPosition(); + if (parenPosition >= 0 && offset <= parenPosition) { + if (offset < parenPosition) + scribe.restartAtOffset(parenPosition); + scribe.pendingSpace = false; + scribe.printNextToken(Token.tRPAREN, spaceBeforeClosingParen); + } + if (continuationFormatter != null) + continuationFormatter.run(); + } + } + { shouldVisitNames = true; shouldVisitDeclarations = true; @@ -1069,10 +1182,10 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, } final ICPPASTTemplateParameter[] templateParameters= node.getTemplateParameters(); if (templateParameters.length > 0) { - final ListAlignment align= new ListAlignment(Alignment.M_COMPACT_SPLIT); - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_template_parameters; - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_template_parameters; - formatList(Arrays.asList(templateParameters), align, false, false); + final ListOptions options= new ListOptions(Alignment.M_COMPACT_SPLIT); + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_template_parameters; + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_template_parameters; + formatList(Arrays.asList(templateParameters), options, false, false, null); } scribe.printNextToken(new int[] { Token.tGT, Token.tSHIFTR }, preferences.insert_space_before_closing_angle_bracket_in_template_parameters); if (preferences.insert_space_after_closing_angle_bracket_in_template_parameters) { @@ -1158,9 +1271,9 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, scribe.startNewLine(); scribe.indentForContinuation(); } - final ListAlignment align= new ListAlignment(preferences.alignment_for_constructor_initializer_list); - align.fTieBreakRule = Alignment.R_OUTERMOST; - formatList(Arrays.asList(constructorChain), align, false, false); + final ListOptions options= new ListOptions(preferences.alignment_for_constructor_initializer_list); + options.fTieBreakRule = Alignment.R_OUTERMOST; + formatList(Arrays.asList(constructorChain), options, false, false, null); scribe.unIndentForContinuation(); } } @@ -1203,28 +1316,30 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, } private int visit(ICPPASTFunctionDeclarator node) { - visit((IASTStandardFunctionDeclarator) node); + final List parameters = Arrays.asList(node.getParameters()); + final ListOptions options = createListOptionsForFunctionParameters(); + formatList(parameters, options, true, node.takesVarArgs(), + new CPPFunctionDeclaratorTailFormatter(node, scribe.getTailFormatter())); - boolean needSpace = skipConstVolatileRestrict(); - - final IASTTypeId[] exceptionSpecification= node.getExceptionSpecification(); - if (exceptionSpecification != null) { - if (peekNextToken() == Token.t_throw) { + IASTFileLocation fileLocation= node.getFileLocation(); + if (fileLocation != null && + scribe.scanner.getCurrentPosition() < fileLocation.getNodeOffset() + fileLocation.getNodeLength()) { + skipConstVolatileRestrict(); + + final IASTTypeId[] exceptionSpecification= node.getExceptionSpecification(); + if (exceptionSpecification != null && peekNextToken() == Token.t_throw) formatExceptionSpecification(exceptionSpecification); - needSpace = false; - } - } - // skip the rest (=0) - if (needSpace && scribe.printComment()) { + // Skip the rest (=0) + scribe.printTrailingComment(); scribe.space(); + skipNode(node); } - skipNode(node); return PROCESS_SKIP; } private void formatExceptionSpecification(final IASTTypeId[] exceptionSpecification) { if (exceptionSpecification.length > 0) { - Alignment alignment =scribe.createAlignment( + Alignment alignment = scribe.createAlignment( Alignment.EXCEPTION_SPECIFICATION, preferences.alignment_for_throws_clause_in_method_declaration, exceptionSpecification.length, @@ -1279,19 +1394,43 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, private int visit(IASTStandardFunctionDeclarator node) { final List parameters = Arrays.asList(node.getParameters()); - final ListAlignment align= new ListAlignment(preferences.alignment_for_parameters_in_method_declaration); - align.fSpaceBeforeOpeningParen= preferences.insert_space_before_opening_paren_in_method_declaration; - align.fSpaceAfterOpeningParen= preferences.insert_space_after_opening_paren_in_method_declaration; - align.fSpaceBeforeClosingParen= preferences.insert_space_before_closing_paren_in_method_declaration; - align.fSpaceBetweenEmptyParen= preferences.insert_space_between_empty_parens_in_method_declaration; - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_method_declaration_parameters; - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_method_declaration_parameters; - align.fTieBreakRule = Alignment.R_OUTERMOST; - formatList(parameters, align, true, node.takesVarArgs()); - + final ListOptions options = createListOptionsForFunctionParameters(); + formatList(parameters, options, true, node.takesVarArgs(), new TrailingSemicolonFormatter(node)); return PROCESS_SKIP; } + private ListOptions createListOptionsForFunctionParameters() { + final ListOptions options= new ListOptions(preferences.alignment_for_parameters_in_method_declaration); + options.fSpaceBeforeOpeningParen= preferences.insert_space_before_opening_paren_in_method_declaration; + options.fSpaceAfterOpeningParen= preferences.insert_space_after_opening_paren_in_method_declaration; + options.fSpaceBeforeClosingParen= preferences.insert_space_before_closing_paren_in_method_declaration; + options.fSpaceBetweenEmptyParen= preferences.insert_space_between_empty_parens_in_method_declaration; + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_method_declaration_parameters; + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_method_declaration_parameters; + options.fTieBreakRule = Alignment.R_OUTERMOST; + return options; + } + + /** + * Returns the position of the last character of a node, or -1 if that character is part of + * a macro expansion. + * + * @param node an AST node + * @return the position of the last character of a node, or -1 if that character is part of + * a macro expansion. + */ + private static int getLastNodeCharacterPosition(IASTNode node) { + IASTNodeLocation[] locations= node.getNodeLocations(); + if (locations.length > 0) { + IASTNodeLocation lastLocation = locations[locations.length - 1]; + if (!(lastLocation instanceof IASTMacroExpansionLocation)) { + IASTFileLocation fileLocation= lastLocation.asFileLocation(); + return fileLocation.getNodeOffset() + fileLocation.getNodeLength() - 1; + } + } + return -1; + } + private void formatPointers(IASTPointerOperator[] pointers) { for (IASTPointerOperator pointer : pointers) { if (scribe.printComment()) { @@ -1323,13 +1462,13 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, private int visit(ICASTKnRFunctionDeclarator node) { final List parameters= Arrays.asList(node.getParameterNames()); - ListAlignment align= new ListAlignment(preferences.alignment_for_parameters_in_method_declaration); - align.fSpaceAfterOpeningParen= preferences.insert_space_after_opening_paren_in_method_declaration; - align.fSpaceBeforeClosingParen= preferences.insert_space_before_closing_paren_in_method_declaration; - align.fSpaceBetweenEmptyParen= preferences.insert_space_between_empty_parens_in_method_declaration; - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_method_declaration_parameters; - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_method_declaration_parameters; - formatList(parameters, align, true, false); + ListOptions options= new ListOptions(preferences.alignment_for_parameters_in_method_declaration); + options.fSpaceAfterOpeningParen= preferences.insert_space_after_opening_paren_in_method_declaration; + options.fSpaceBeforeClosingParen= preferences.insert_space_before_closing_paren_in_method_declaration; + options.fSpaceBetweenEmptyParen= preferences.insert_space_between_empty_parens_in_method_declaration; + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_method_declaration_parameters; + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_method_declaration_parameters; + formatList(parameters, options, true, false, null); IASTDeclaration[] parameterDecls= node.getParameterDeclarations(); scribe.startNewLine(); @@ -1405,38 +1544,16 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, scribe.space(); } } - final ListAlignment align= new ListAlignment(preferences.alignment_for_declarator_list); - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_declarator_list; - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_declarator_list; - formatList(declarators, align, false, false); - } - if (fExpectSemicolonAfterDeclaration) { - handleNodeEndingInSemicolon(node); - if (peekNextToken() == Token.tSEMI) { - scribe.printNextToken(Token.tSEMI, fInsideFor ? preferences.insert_space_before_semicolon_in_for : preferences.insert_space_before_semicolon); - scribe.printTrailingComment(); - } + final ListOptions options= new ListOptions(preferences.alignment_for_declarator_list); + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_declarator_list; + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_declarator_list; + Runnable tailFormatter = fExpectSemicolonAfterDeclaration ? + new TrailingSemicolonFormatter(node) : null; + formatList(declarators, options, false, false, tailFormatter); } return PROCESS_SKIP; } - private void handleNodeEndingInSemicolon(IASTSimpleDeclaration node) { - if (scribe.skipRange() && peekNextToken(true) == Token.tSEMI) { - IASTNodeLocation[] locations= node.getNodeLocations(); - if (locations.length > 0) { - IASTNodeLocation lastLocation = locations[locations.length - 1]; - if (!(lastLocation instanceof IASTMacroExpansionLocation)) { - IASTFileLocation fileLocation= lastLocation.asFileLocation(); - int startOffset= fileLocation.getNodeOffset(); - int currentPosition= scribe.scanner.getCurrentPosition(); - if (currentPosition >= startOffset) { - scribe.restartAtOffset(startOffset); - } - } - } - } - } - private int visit(ICPPASTTemplateDeclaration node) { if (node.isExported()) { scribe.printNextToken(Token.t_export); @@ -1451,10 +1568,10 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, // template parameters final ICPPASTTemplateParameter[] templateParameters= node.getTemplateParameters(); if (templateParameters.length > 0) { - final ListAlignment align= new ListAlignment(Alignment.M_COMPACT_SPLIT); - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_template_parameters; - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_template_parameters; - formatList(Arrays.asList(templateParameters), align, false, false); + final ListOptions options= new ListOptions(Alignment.M_COMPACT_SPLIT); + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_template_parameters; + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_template_parameters; + formatList(Arrays.asList(templateParameters), options, false, false, null); } scribe.printNextToken(new int[] { Token.tGT, Token.tSHIFTR }, preferences.insert_space_before_closing_angle_bracket_in_template_parameters); if (preferences.insert_space_after_closing_angle_bracket_in_template_parameters) { @@ -1624,10 +1741,10 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, } catch (UnsupportedOperationException exc) { } catch (ExpansionOverlapsBoundaryException exc) { } - final ListAlignment align= new ListAlignment(preferences.alignment_for_base_clause_in_type_declaration); - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_base_types; - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_base_types; - formatList(baseSpecifiers, align, false, false); + final ListOptions options= new ListOptions(preferences.alignment_for_base_clause_in_type_declaration); + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_base_types; + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_base_types; + formatList(baseSpecifiers, options, false, false, null); } // member declarations @@ -1774,16 +1891,16 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, final int enumIndent= scribe.numberOfIndentations; final IASTEnumerator[] enumerators= node.getEnumerators(); - final ListAlignment align= new ListAlignment(preferences.alignment_for_enumerator_list); - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_enum_declarations; - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_enum_declarations; - align.fContinuationIndentation= enumIndent == headerIndent ? 1 : 0; - formatList(Arrays.asList(enumerators), align, false, false); + final ListOptions options= new ListOptions(preferences.alignment_for_enumerator_list); + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_enum_declarations; + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_enum_declarations; + options.fContinuationIndentation= enumIndent == headerIndent ? 1 : 0; + formatList(Arrays.asList(enumerators), options, false, false, null); // handle trailing comma if (peekNextToken() == Token.tCOMMA) { - scribe.printNextToken(Token.tCOMMA, align.fSpaceBeforeComma); - if (align.fSpaceAfterComma) { + scribe.printNextToken(Token.tCOMMA, options.fSpaceBeforeComma); + if (options.fSpaceAfterComma) { scribe.space(); } } @@ -1800,97 +1917,86 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, /** * Format a given list of elements according alignment options. * - * @param elements - * @param align - * @param encloseInParen - * @param addEllipsis + * @param elements the elements to format + * @param options formatting options + * @param encloseInParen indicates whether the list should be enclosed in parentheses + * @param addEllipsis indicates whether ellipsis should be added after the last element + * @param tailFormatter formatter for the trailing text that should be kept together with + * the last element of the list. */ - private void formatList(List elements, ListAlignment align, - boolean encloseInParen, boolean addEllipsis) { + private void formatList(List elements, ListOptions options, + boolean encloseInParen, boolean addEllipsis, Runnable tailFormatter) { if (encloseInParen) - scribe.printNextToken(Token.tLPAREN, align.fSpaceBeforeOpeningParen); + scribe.printNextToken(Token.tLPAREN, options.fSpaceBeforeOpeningParen); final int elementsLength = elements.size(); + if (encloseInParen) { + boolean spaceBeforeClosingParen = elements.isEmpty() && !addEllipsis ? + options.fSpaceBetweenEmptyParen : options.fSpaceBeforeClosingParen; + tailFormatter = new ClosingParensesisTailFormatter(spaceBeforeClosingParen, tailFormatter); + } + if (!elements.isEmpty() || addEllipsis) { - if (align.fSpaceAfterOpeningParen) { + if (options.fSpaceAfterOpeningParen) { scribe.space(); } - final int continuationIndentation= align.fContinuationIndentation >= 0 ? - align.fContinuationIndentation : preferences.continuation_indentation; - Alignment listAlignment = scribe.createAlignment( + final int continuationIndentation= options.fContinuationIndentation >= 0 ? + options.fContinuationIndentation : preferences.continuation_indentation; + Alignment alignment = scribe.createAlignment( Alignment.LIST_ELEMENTS_PREFIX + (elements.isEmpty() ? "ellipsis" : elements.get(0).getClass().getSimpleName()), //$NON-NLS-1$ - align.fMode, - align.fTieBreakRule, + options.fMode, + options.fTieBreakRule, elementsLength + (addEllipsis ? 1 : 0), scribe.scanner.getCurrentPosition(), continuationIndentation, false); - scribe.enterAlignment(listAlignment); + scribe.enterAlignment(alignment); boolean ok = false; do { try { int i; for (i = 0; i < elementsLength; i++) { - if (i > 0) { - // handle missing parameter - int token= peekNextToken(); - if (token == Token.tIDENTIFIER) { - if (!scribe.skipToToken(Token.tCOMMA)) { - break; - } - } else if (token == Token.tRPAREN) { - if (encloseInParen) { - break; - } - if (!scribe.skipToToken(Token.tCOMMA)) { - break; - } - } - scribe.printNextToken(Token.tCOMMA, align.fSpaceBeforeComma); - scribe.printTrailingComment(); - } - scribe.alignFragment(listAlignment, i); - if (i > 0 && align.fSpaceAfterComma) { - scribe.space(); - } final IASTNode node= elements.get(i); + if (i < alignment.fragmentCount - 1) { + scribe.setTailFormatter( + new TrailingCommaFormatter(options.fSpaceBeforeComma, + options.fSpaceAfterComma)); + } else { + scribe.setTailFormatter(tailFormatter); + } + scribe.alignFragment(alignment, i); if (node instanceof ICPPASTConstructorChainInitializer) { - // this is a special case + // Constructor chain initializer is a special case. visit((ICPPASTConstructorChainInitializer) node); } else { node.accept(this); } + if (i < alignment.fragmentCount - 1) { + scribe.runTailFormatter(); + } } if (addEllipsis) { if (i > 0) { - scribe.printNextToken(Token.tCOMMA, align.fSpaceBeforeComma); + scribe.printNextToken(Token.tCOMMA, options.fSpaceBeforeComma); scribe.printTrailingComment(); } - scribe.alignFragment(listAlignment, i); - if (i > 0 && align.fSpaceAfterComma) { + scribe.alignFragment(alignment, i); + if (i > 0 && options.fSpaceAfterComma) { scribe.space(); } scribe.printNextToken(Token.tELIPSE); } + scribe.runTailFormatter(); ok = true; } catch (AlignmentException e) { scribe.redoAlignment(e); } catch (ASTProblemException e) { } } while (!ok); - scribe.exitAlignment(listAlignment, true); - } - if (encloseInParen) { - // handle missing parameter - if (peekNextToken() == Token.tIDENTIFIER) { - scribe.skipToToken(Token.tRPAREN); - } - if (elementsLength == 0 && !addEllipsis) { - scribe.printNextToken(Token.tRPAREN, align.fSpaceBetweenEmptyParen); - } else { - scribe.printNextToken(Token.tRPAREN, align.fSpaceBeforeClosingParen); - } + scribe.exitAlignment(alignment, true); + } else if (tailFormatter != null) { + tailFormatter.run(); } } @@ -1958,17 +2064,17 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, if (preferences.insert_space_before_question_in_conditional) { scribe.space(); } - Alignment conditionalExpressionAlignment = scribe.createAlignment( + Alignment alignment = scribe.createAlignment( Alignment.CONDITIONAL_EXPRESSION, preferences.alignment_for_conditional_expression, 2, scribe.scanner.getCurrentPosition()); - scribe.enterAlignment(conditionalExpressionAlignment); + scribe.enterAlignment(alignment); boolean ok = false; do { try { - scribe.alignFragment(conditionalExpressionAlignment, 0); + scribe.alignFragment(alignment, 0); scribe.printNextToken(Token.tQUESTION, false); if (preferences.insert_space_after_question_in_conditional) { @@ -1979,7 +2085,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, positiveExpression.accept(this); } scribe.printTrailingComment(); - scribe.alignFragment(conditionalExpressionAlignment, 1); + scribe.alignFragment(alignment, 1); scribe.printNextToken(Token.tCOLON, preferences.insert_space_before_colon_in_conditional); if (preferences.insert_space_after_colon_in_conditional) { @@ -1992,7 +2098,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, scribe.redoAlignment(e); } } while (!ok); - scribe.exitAlignment(conditionalExpressionAlignment, true); + scribe.exitAlignment(alignment, true); return PROCESS_SKIP; } @@ -2023,26 +2129,26 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, if (args != null) { expressions= Arrays.asList(args); } else { - // no arguments + // No arguments expressions= Collections.emptyList(); } - final ListAlignment align= new ListAlignment(preferences.alignment_for_arguments_in_method_invocation); - align.fSpaceBeforeOpeningParen= preferences.insert_space_before_opening_paren_in_method_invocation; - align.fSpaceAfterOpeningParen= preferences.insert_space_after_opening_paren_in_method_invocation; - align.fSpaceBeforeClosingParen= preferences.insert_space_before_closing_paren_in_method_invocation; - align.fSpaceBetweenEmptyParen= preferences.insert_space_between_empty_parens_in_method_invocation; - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_method_invocation_arguments; - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_method_invocation_arguments; - align.fTieBreakRule = Alignment.R_OUTERMOST; - formatList(expressions, align, true, false); + final ListOptions options= new ListOptions(preferences.alignment_for_arguments_in_method_invocation); + options.fSpaceBeforeOpeningParen= preferences.insert_space_before_opening_paren_in_method_invocation; + options.fSpaceAfterOpeningParen= preferences.insert_space_after_opening_paren_in_method_invocation; + options.fSpaceBeforeClosingParen= preferences.insert_space_before_closing_paren_in_method_invocation; + options.fSpaceBetweenEmptyParen= preferences.insert_space_between_empty_parens_in_method_invocation; + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_method_invocation_arguments; + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_method_invocation_arguments; + options.fTieBreakRule = Alignment.R_OUTERMOST; + formatList(expressions, options, true, false, scribe.getTailFormatter()); } private int visit(IASTExpressionList node) { final List expressions = Arrays.asList(node.getExpressions()); - final ListAlignment align= new ListAlignment(preferences.alignment_for_expression_list); - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_expression_list; - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_expression_list; - formatList(expressions, align, false, false); + final ListOptions options= new ListOptions(preferences.alignment_for_expression_list); + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_expression_list; + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_expression_list; + formatList(expressions, options, false, false, null); return PROCESS_SKIP; } @@ -2206,16 +2312,16 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, scribe.space(); } - final ListAlignment align= new ListAlignment(preferences.alignment_for_expressions_in_initializer_list); - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_initializer_list; - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_initializer_list; - align.fContinuationIndentation= preferences.continuation_indentation_for_initializer_list; - formatList(initializers, align, false, false); + final ListOptions options= new ListOptions(preferences.alignment_for_expressions_in_initializer_list); + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_initializer_list; + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_initializer_list; + options.fContinuationIndentation= preferences.continuation_indentation_for_initializer_list; + formatList(initializers, options, false, false, null); // handle trailing comma if (peekNextToken() == Token.tCOMMA) { - scribe.printNextToken(Token.tCOMMA, align.fSpaceBeforeComma); - if (align.fSpaceAfterComma) { + scribe.printNextToken(Token.tCOMMA, options.fSpaceBeforeComma); + if (options.fSpaceAfterComma) { scribe.space(); } } @@ -2665,10 +2771,17 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, } private int visit(IASTExpressionStatement node) { - node.getExpression().accept(this); + Runnable semicolonFormatter = null; + if (!fInsideFor) { + semicolonFormatter = new TrailingSemicolonFormatter(node); + scribe.setTailFormatter(semicolonFormatter); + } + node.getExpression().accept(this); + if (semicolonFormatter != null) { + semicolonFormatter.run(); + scribe.setTailFormatter(null); + } if (!fInsideFor) { - scribe.printNextToken(Token.tSEMI, preferences.insert_space_before_semicolon); - scribe.printTrailingComment(); scribe.startNewLine(); } return PROCESS_SKIP; @@ -2896,6 +3009,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, scribe.printNextToken(Token.t_else, true); } } + if (elseStatement instanceof IASTCompoundStatement) { elseStatement.accept(this); } else if (elseStatement instanceof IASTIfStatement) { @@ -2926,7 +3040,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, scribe.printNextToken(Token.tCOLONCOLON); } IASTName[] names= node.getNames(); - for (int i = 0; i < names.length-1; i++) { + for (int i = 0; i < names.length - 1; i++) { names[i].accept(this); scribe.printNextToken(Token.tCOLONCOLON); } @@ -2934,7 +3048,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, // destructor scribe.printNextToken(Token.tCOMPL, false); } - names[names.length-1].accept(this); + names[names.length - 1].accept(this); return PROCESS_SKIP; } @@ -2948,10 +3062,10 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, int angleBrackets = fOpenAngleBrackets++; final IASTNode[] templateArguments= node.getTemplateArguments(); if (templateArguments.length > 0) { - final ListAlignment align= new ListAlignment(Alignment.M_COMPACT_SPLIT); - align.fSpaceAfterComma= preferences.insert_space_after_comma_in_template_arguments; - align.fSpaceBeforeComma= preferences.insert_space_before_comma_in_template_arguments; - formatList(Arrays.asList(templateArguments), align, false, false); + final ListOptions options= new ListOptions(Alignment.M_COMPACT_SPLIT); + options.fSpaceAfterComma= preferences.insert_space_after_comma_in_template_arguments; + options.fSpaceBeforeComma= preferences.insert_space_before_comma_in_template_arguments; + formatList(Arrays.asList(templateArguments), options, false, false, null); } if (peekNextToken() == Token.tSHIFTR) { if (fOpenAngleBrackets == angleBrackets + 2) { @@ -3445,7 +3559,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, IASTNodeLocation[] locations= node.getNodeLocations(); if (locations.length == 0) { } else if (node instanceof IASTProblemHolder) { - } else if (locations[locations.length-1] instanceof IASTMacroExpansionLocation) { + } else if (locations[locations.length - 1] instanceof IASTMacroExpansionLocation) { return true; } return false; diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java index c1c05d3cc20..1c7b922c552 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java @@ -49,6 +49,9 @@ public class Scribe { public Alignment currentAlignment; public Alignment memberAlignment; public AlignmentException currentAlignmentException; + + /** @see Alignment#tailFormatter */ + private Runnable tailFormatter; public Token currentToken; @@ -869,7 +872,7 @@ public class Scribe { } private void print(int length, boolean considerSpaceIfAny) { - if (checkLineWrapping && length + column > pageWidth) { + if (checkLineWrapping && length + column - 1 > pageWidth) { handleLineTooLong(); } lastNumberOfNewLines= 0; @@ -882,6 +885,9 @@ public class Scribe { if (pendingSpace) { addInsertEdit(scanner.getCurrentTokenStartPosition(), SPACE); } + if (checkLineWrapping && length + column - 1 > pageWidth) { + handleLineTooLong(); + } pendingSpace= false; column += length; needSpace= true; @@ -1544,7 +1550,7 @@ public class Scribe { } public void printNewLine() { - printNewLine(scanner.getCurrentTokenEndPosition() + 1); + printNewLine(scanner.getCurrentPosition()); } public void printNewLine(int insertPosition) { @@ -1950,7 +1956,7 @@ public class Scribe { } /** - * Skip to the next occurrence of the given token type. + * Skips to the next occurrence of the given token type. * If successful, the next token will be the expected token, * otherwise the scanner position is left unchanged. * @@ -1962,56 +1968,96 @@ public class Scribe { if (shouldSkip(skipStart)) { return true; } - int braceLevel= 0; - int parenLevel= 0; - switch (expectedTokenType) { - case Token.tRBRACE: - ++braceLevel; - break; - case Token.tRPAREN: - ++parenLevel; - break; + int tokenStart = findToken(expectedTokenType); + if (tokenStart < 0) { + return false; } - while ((currentToken= scanner.nextToken()) != null) { - switch (currentToken.type) { - case Token.tLBRACE: - if (expectedTokenType != Token.tLBRACE) { - ++braceLevel; - } - break; + printRaw(skipStart, tokenStart - skipStart); + currentToken= scanner.nextToken(); + scanner.resetTo(tokenStart, scannerEndPosition - 1); + return true; + } + + /** + * Searches for the next occurrence of the given token type. + * If successful, returns the offset of the found token, otherwise -1. + * The scanner position is left unchanged. + * + * @param tokenType type of the token to look for + * @return true if a matching token was found + */ + public int findToken(int tokenType) { + return findToken(tokenType, scannerEndPosition - 1); + } + + /** + * Searches for the next occurrence of the given token type. + * If successful, returns the offset of the found token, otherwise -1. + * The scanner position is left unchanged. + * + * @param tokenType type of the token to look for + * @param endPosition end position limiting the search + * @return true if a matching token was found + */ + public int findToken(int tokenType, int endPosition) { + int startPosition= scanner.getCurrentPosition(); + if (startPosition >= endPosition) { + return -1; + } + try { + int braceLevel= 0; + int parenLevel= 0; + switch (tokenType) { case Token.tRBRACE: - --braceLevel; - break; - case Token.tLPAREN: - if (expectedTokenType != Token.tLPAREN) { - ++parenLevel; - } + ++braceLevel; break; case Token.tRPAREN: - --parenLevel; + ++parenLevel; break; - case Token.tWHITESPACE: - case Token.tLINECOMMENT: - case Token.tBLOCKCOMMENT: - case Token.tPREPROCESSOR: - case Token.tPREPROCESSOR_DEFINE: - case Token.tPREPROCESSOR_INCLUDE: - continue; } - if (braceLevel <= 0 && parenLevel <= 0) { - if (currentToken.type == expectedTokenType) { - int tokenStart= scanner.getCurrentTokenStartPosition(); - printRaw(skipStart, tokenStart - skipStart); - scanner.resetTo(tokenStart, scannerEndPosition - 1); - return true; + Token token; + while ((token= scanner.nextToken()) != null) { + if (scanner.getCurrentTokenEndPosition() > endPosition) + return -1; + + switch (token.type) { + case Token.tLBRACE: + if (tokenType != Token.tLBRACE) { + ++braceLevel; + } + break; + case Token.tRBRACE: + --braceLevel; + break; + case Token.tLPAREN: + if (tokenType != Token.tLPAREN) { + ++parenLevel; + } + break; + case Token.tRPAREN: + --parenLevel; + break; + case Token.tWHITESPACE: + case Token.tLINECOMMENT: + case Token.tBLOCKCOMMENT: + case Token.tPREPROCESSOR: + case Token.tPREPROCESSOR_DEFINE: + case Token.tPREPROCESSOR_INCLUDE: + continue; + } + if (braceLevel <= 0 && parenLevel <= 0) { + if (token.type == tokenType) { + return scanner.getCurrentTokenStartPosition(); + } + } + if (braceLevel < 0 || parenLevel < 0) { + break; } } - if (braceLevel < 0 || parenLevel < 0) { - break; - } + } finally { + scanner.resetTo(startPosition, scannerEndPosition - 1); } - scanner.resetTo(skipStart, scannerEndPosition - 1); - return false; + return -1; } public boolean printCommentPreservingNewLines() { @@ -2073,4 +2119,41 @@ public class Scribe { } } } + + /* + * Returns the tail formatter associated with the current alignment or, if there is no current + * alignment, with the Scribe itself. + * @see #tailFormatter + */ + public Runnable getTailFormatter() { + if (currentAlignment != null) { + return currentAlignment.tailFormatter; + } else { + return this.tailFormatter; + } + } + + /* + * Sets the tail formatter associated with the current alignment or, if there is no current + * alignment, with the Scribe itself. + * @see #tailFormatter + */ + public void setTailFormatter(Runnable tailFormatter) { + if (currentAlignment != null) { + currentAlignment.tailFormatter = tailFormatter; + } else { + this.tailFormatter = tailFormatter; + } + } + + /* + * Runs the tail formatter associated with the current alignment or, if there is no current + * alignment, with the Scribe itself. + * @see #tailFormatter + */ + public void runTailFormatter() { + Runnable formatter = getTailFormatter(); + if (formatter != null) + formatter.run(); + } } diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/align/Alignment.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/align/Alignment.java index 093076c1be8..d4e187a31da 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/align/Alignment.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/align/Alignment.java @@ -33,26 +33,32 @@ public class Alignment { public static final String MACRO_ARGUMENTS = "macroArguments"; //$NON-NLS-1$ public static final String LIST_ELEMENTS_PREFIX = "listElements_"; //$NON-NLS-1$ - // name of alignment + /** The name of the alignment */ public String name; - // link to enclosing alignment + /** Link to the enclosing alignment. */ public Alignment enclosing; - // start location of this alignment + /** Start location of this alignment. */ public Location location; + + /** + * Tail formatter is an encapsulation mechanism for formatting of the trailing text that should + * be kept together with the last element of a list. + */ + public Runnable tailFormatter; - // indentation management + // Indentation management public int fragmentIndex; public int fragmentCount; public int[] fragmentIndentations; public boolean needRedoColumnAlignment; - // chunk management + // Chunk management public int chunkStartIndex; public int chunkKind; - // break management + // Break management public int originalIndentationLevel; public int breakIndentationLevel; public int shiftBreakIndentationLevel; @@ -68,7 +74,7 @@ public class Alignment { public static final int M_INDENT_ON_COLUMN = 2; // if bit set, broken fragments will be aligned on current location column (default is to break at current indentation level) public static final int M_INDENT_BY_ONE = 4; // if bit set, broken fragments will be indented one level below current (not using continuation indentation) - // split modes can be combined either with M_FORCE or M_INDENT_ON_COLUMN + // Split modes can be combined either with M_FORCE or M_INDENT_ON_COLUMN /** foobar(#fragment1, #fragment2,
    *
  • #fragment3, #fragment4
  • @@ -113,7 +119,7 @@ public class Alignment { //64+32 //64+32+16 - // mode controlling column alignments + // Mode controlling column alignments /** * * @@ -129,23 +135,24 @@ public class Alignment { public static final int SPLIT_MASK = M_ONE_PER_LINE_SPLIT | M_NEXT_SHIFTED_SPLIT | M_COMPACT_SPLIT | M_COMPACT_FIRST_BREAK_SPLIT | M_NEXT_PER_LINE_SPLIT; - // alignment tie-break rules - when split is needed, will decide whether innermost/outermost alignment is to be chosen + // Alignment tie-break rules - when split is needed, will decide whether innermost/outermost alignment is to be chosen public static final int R_OUTERMOST = 1; public static final int R_INNERMOST = 2; public int tieBreakRule; - // alignment effects on a per fragment basis + // Alignment effects on a per fragment basis public static int NONE = 0; public static int BREAK = 1; - // chunk kind + // Chunk kind public static final int CHUNK_FIELD = 1; public static final int CHUNK_METHOD = 2; public static final int CHUNK_TYPE = 3; public static final int CHUNK_ENUM = 4; - // location to align and break on. - public Alignment(String name, int mode, int tieBreakRule, Scribe scribe, int fragmentCount, int sourceRestart, int continuationIndent) { + // Location to align and break on. + public Alignment(String name, int mode, int tieBreakRule, Scribe scribe, int fragmentCount, + int sourceRestart, int continuationIndent) { this.name = name; this.location = new Location(scribe, sourceRestart); this.mode = mode; diff --git a/core/org.eclipse.cdt.ui.tests/resources/formatter/preview/After.cpp b/core/org.eclipse.cdt.ui.tests/resources/formatter/preview/After.cpp index 6560d515aee..853c365b125 100644 --- a/core/org.eclipse.cdt.ui.tests/resources/formatter/preview/After.cpp +++ b/core/org.eclipse.cdt.ui.tests/resources/formatter/preview/After.cpp @@ -44,8 +44,8 @@ int foo(int bar) const { /* * Line Wrapping */ -int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 1000, 2000, 3000, 4000, 5000 }; +int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 1000, + 2000, 3000, 4000, 5000 }; int compare(int argument, int otherArg) { return argument + otherArg > argument * otherArg + 1000000 ? 100000 + 50000 : 200000 - 30000; 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 65f9ead673d..fc6e5a8088f 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 @@ -953,6 +953,59 @@ public class CodeFormatterTest extends BaseUITestCase { assertFormatterResult(); } + //void f1(const char* long_parameter_name, int very_looooooooooong_parameter_name, int another_parameter_name ); + //void f2(const char* long_parameter_name,int very_loooooooooooong_parameter_name, int another_parameter_name ) ; + //void f3(const char* long_parameter_name,int very_loooooooooooong_parameter_name,int very_loong_parameter_name) ; + //void f4(const char* long_parameter_name, int very_loooooooooooong_parameter_name,int very_looong_parameter_name) ; + + //void f1(const char* long_parameter_name, int very_looooooooooong_parameter_name, + // int another_parameter_name); + //void f2(const char* long_parameter_name, + // int very_loooooooooooong_parameter_name, int another_parameter_name); + //void f3(const char* long_parameter_name, + // int very_loooooooooooong_parameter_name, int very_loong_parameter_name); + //void f4(const char* long_parameter_name, + // int very_loooooooooooong_parameter_name, + // int very_looong_parameter_name); + public void testFunctionDeclaration() throws Exception { + fOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, CCorePlugin.SPACE); + fOptions.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION, + Integer.toString(Alignment.M_NEXT_PER_LINE_SPLIT | Alignment.M_INDENT_ON_COLUMN)); + assertFormatterResult(); + } + + //int f1(int a, int b, int c, int d, int e, int f, int g); + //int f2(int a, int b, int c, int d, int e, int f, int g); + // + //void test() { + //f1(100000000,200000000,300000000,400000000,500000000,600000000,70000); + //f1(100000000,200000000,300000000,400000000,500000000,600000000,700000); + //f1(100000,200000,300000,400000,500000,600000,f2(1,2,3,4,5,6,7)); + //f1(100000,200000,300000,400000,500000,600000,f2(1,2,3,4,5,6,70)); + //f1(100000,200000,300000,400000,500000,f2(10,20,30,40,50,60,7000),700000); + //f1(100000,200000,300000,400000,500000,f2(10,20,30,40,50,60,70000),700000); + //} + + //int f1(int a, int b, int c, int d, int e, int f, int g); + //int f2(int a, int b, int c, int d, int e, int f, int g); + // + //void test() { + // f1(100000000, 200000000, 300000000, 400000000, 500000000, 600000000, 70000); + // f1(100000000, 200000000, 300000000, 400000000, 500000000, 600000000, + // 700000); + // f1(100000, 200000, 300000, 400000, 500000, 600000, f2(1, 2, 3, 4, 5, 6, 7)); + // f1(100000, 200000, 300000, 400000, 500000, 600000, + // f2(1, 2, 3, 4, 5, 6, 70)); + // f1(100000, 200000, 300000, 400000, 500000, f2(10, 20, 30, 40, 50, 60, 7000), + // 700000); + // f1(100000, 200000, 300000, 400000, 500000, + // f2(10, 20, 30, 40, 50, 60, 70000), 700000); + //} + public void testFunctionCall() throws Exception { + fOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, CCorePlugin.SPACE); + assertFormatterResult(); + } + //#define MY_MACRO int a; \ // int b; \ // int c();
    #fragment1A #fragment2A #fragment3A #very-long-fragment4A