diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommentTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommentTests.java index ca8d20b7f90..ded306768b4 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommentTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommentTests.java @@ -29,14 +29,14 @@ public class CommentTests extends AST2TestBase { return suite(CommentTests.class); } - public void testCountCommentsInHeaderFile() throws ParserException{ + public void testCountCommentsInHeaderFile() throws ParserException { IASTTranslationUnit tu = parse(getHSource(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); assertEquals(9, comments.length); } - public void testCommentsInHeaderFile() throws ParserException{ + public void testCommentsInHeaderFile() throws ParserException { IASTTranslationUnit tu = parse(getHSource(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); @@ -51,14 +51,14 @@ public class CommentTests extends AST2TestBase { assertEquals("//Endcomment h", new String(comments[8].getComment())); } - public void testCountCommentsInCPPFile() throws ParserException{ + public void testCountCommentsInCPPFile() throws ParserException { IASTTranslationUnit tu = parse(getCppSource(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); assertEquals(10, comments.length); } - public void testCommentsInCPPFile() throws ParserException{ + public void testCommentsInCPPFile() throws ParserException { IASTTranslationUnit tu = parse(getCppSource(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); @@ -74,14 +74,14 @@ public class CommentTests extends AST2TestBase { assertEquals("//An integer", new String(comments[9].getComment())); } - public void testCountCommentsInCFile() throws ParserException{ + public void testCountCommentsInCFile() throws ParserException { IASTTranslationUnit tu = parse(getCSource(), ParserLanguage.C, false, true); IASTComment[] comments = tu.getComments(); assertEquals(4, comments.length); } - public void testCommentsInCFile() throws ParserException{ + public void testCommentsInCFile() throws ParserException { IASTTranslationUnit tu = parse(getCSource(), ParserLanguage.C, false, true); IASTComment[] comments = tu.getComments(); @@ -211,7 +211,7 @@ public class CommentTests extends AST2TestBase { } // //comment - public void testCommentLocation_bug186337() throws Exception{ + public void testCommentLocation_bug186337() throws Exception { CharSequence code= getContents(1)[0]; IASTTranslationUnit tu = parse(code.toString(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); @@ -234,7 +234,6 @@ public class CommentTests extends AST2TestBase { // #ifdef WHATEVA // TODO: ignored // #endif // TODO: ignored // // TODO: shows up in task list - public void testCommentInDirectives_bug192546() throws Exception { CharSequence code= getContents(1)[0]; IASTTranslationUnit tu = parse(code.toString(), ParserLanguage.CPP, false, false); diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/comenthandler/CommentHandlingTest.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/comenthandler/CommentHandlingTest.java index cda751ea9f3..2846fdcb38c 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/comenthandler/CommentHandlingTest.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/comenthandler/CommentHandlingTest.java @@ -30,6 +30,7 @@ import org.eclipse.cdt.core.parser.tests.rewrite.TestSourceFile; import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter; import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; /** * This test tests the behavior of the class ASTCommenter. It checks if the ASTCommenter assigns @@ -158,15 +159,17 @@ public class CommentHandlingTest extends RewriteBaseTest { return output.toString().trim(); } - private String getSignature(IASTNode actNode) { - if (actNode instanceof IASTCompositeTypeSpecifier) { - IASTCompositeTypeSpecifier comp = (IASTCompositeTypeSpecifier) actNode; + private String getSignature(IASTNode node) { + if (node instanceof IASTCompositeTypeSpecifier) { + IASTCompositeTypeSpecifier comp = (IASTCompositeTypeSpecifier) node; return comp.getName().toString(); - } else if (actNode instanceof IASTEnumerationSpecifier) { - IASTEnumerationSpecifier comp = (IASTEnumerationSpecifier) actNode; + } else if (node instanceof IASTEnumerationSpecifier) { + IASTEnumerationSpecifier comp = (IASTEnumerationSpecifier) node; return comp.getName().toString(); + } else if (node instanceof IASTTranslationUnit) { + return Path.fromOSString(node.getFileLocation().getFileName()).lastSegment(); } - return actNode.getRawSignature(); + return node.getRawSignature(); } private static String getSeparatingRegexp() { diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index b4de8014640..bad3554ca8a 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -61,7 +61,7 @@ Export-Package: org.eclipse.cdt.core, org.eclipse.cdt.internal.core.dom.rewrite;x-friends:="org.eclipse.cdt.core.tests,org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.dom.rewrite.astwriter;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.dom.rewrite.changegenerator;x-internal:=true, - org.eclipse.cdt.internal.core.dom.rewrite.commenthandler;x-internal:=true, + org.eclipse.cdt.internal.core.dom.rewrite.commenthandler;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.dom.rewrite.util;x-internal:=true, org.eclipse.cdt.internal.core.envvar;x-friends:="org.eclipse.cdt.ui,org.eclipse.cdt.managedbuilder.core", org.eclipse.cdt.internal.core.index;x-friends:="org.eclipse.cdt.ui", diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTFileLocation.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTFileLocation.java index ef76745892c..440be3149c3 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTFileLocation.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTFileLocation.java @@ -37,25 +37,25 @@ public interface IASTFileLocation extends IASTNodeLocation { public int getNodeLength(); /** - * Get the starting line number. Locations obtained via the index do not have line numbers - * and return 0. + * Returns the starting line number. Locations obtained via the index do not have line numbers + * and return {@code 0}. * - * @return int representing line number or 0 if not applicable + * @return the 1-based line number, or {@code 0} if not applicable */ public int getStartingLineNumber(); /** - * Get the ending line number. Locations obtained via the index do not have line numbers - * and return 0. + * Returns the ending line number. Locations obtained via the index do not have line numbers + * and return {@code 0}. * - * @return int representing line number or 0 if not applicable + * @return the 1-based line number, or {@code 0} if not applicable */ public int getEndingLineNumber(); /** * Returns the inclusion statement that included this file, or null for * a top-level file. - * Also null when the file location does not belong to an AST node, e.g. + * Also {@code null} when the file location does not belong to an AST node, e.g. * if it is obtained from a name in the index. * @since 5.4 */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTForStatement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTForStatement.java index e6bcf447a89..c0020387e7a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTForStatement.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTForStatement.java @@ -11,8 +11,8 @@ package org.eclipse.cdt.core.dom.ast; /** - * The for statement. The initialization clause can be an expression or a - * declaration but not both. + * The 'for' statement. The initialization clause can be an expression + * or a declaration but not both. * * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. @@ -57,8 +57,7 @@ public interface IASTForStatement extends IASTStatement { * @param statement */ public void setInitializerStatement( IASTStatement statement ); - - + /** * Get the condition expression for the loop. * diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTIdExpression.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTIdExpression.java index 9d7934fc4aa..de2a6c26ad4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTIdExpression.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTIdExpression.java @@ -32,7 +32,7 @@ public interface IASTIdExpression extends IASTExpression, IASTNameOwner { public IASTName getName(); /** - * Set the name to be used inthe expression. + * Sets the name to be used in the expression. * * @param name */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java index 0f7ce9af3f1..998f3ada3ee 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java @@ -23,16 +23,14 @@ import org.eclipse.cdt.core.parser.ISignificantMacros; */ public interface IASTPreprocessorIncludeStatement extends IASTPreprocessorStatement, IFileNomination { /** - * INCLUDE_NAME describes the relationship between an include directive and - * it's name. + * {@code INCLUDE_NAME} describes the relationship between an include directive and its name. */ public static final ASTNodeProperty INCLUDE_NAME = new ASTNodeProperty( "IASTPreprocessorMacroDefinition.INCLUDE_NAME - Include Name"); //$NON-NLS-1$ - /** - * Returns the absolute location of the file found through #include. - * Only valid if {@link #isResolved()} returns true. + * Returns the absolute location of the file found through #include, or an empty string if + * include was not resolved. */ public String getPath(); @@ -109,14 +107,14 @@ public interface IASTPreprocessorIncludeStatement extends IASTPreprocessorStatem public boolean isErrorInIncludedFile(); /** - * Returns true, if an attempt will be or has been made to create AST for the target + * Returns {@code true}, if an attempt will be or has been made to create AST for the target * of this inclusion. * @since 5.4 */ public boolean createsAST(); /** - * Returns the file from the index that this include statement has pulled in, or null + * Returns the file from the index that this include statement has pulled in, or {@code null} * if the include creates AST or is unresolved or skipped. * @since 5.4 */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTTranslationUnit.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTTranslationUnit.java index 05d283d8ef5..2b4665d6c5b 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTTranslationUnit.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTTranslationUnit.java @@ -32,26 +32,37 @@ import org.eclipse.core.runtime.IAdaptable; */ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomination, IAdaptable { /** - * OWNED_DECLARATION represents the relationship between an IASTTranslationUnit and - * it's nested IASTDeclaration's. + * {@code OWNED_DECLARATION} represents the relationship between an {@code IASTTranslationUnit} + * and its nested {@code IASTDeclaration}'s. */ public static final ASTNodeProperty OWNED_DECLARATION = new ASTNodeProperty( "IASTTranslationUnit.OWNED_DECLARATION - IASTDeclaration for IASTTranslationUnit"); //$NON-NLS-1$ /** - * SCANNER_PROBLEM represents the relationship between an IASTTranslationUnit and - * it's nested IASTProblem. + * {@code SCANNER_PROBLEM} represents the relationship between an {@code IASTTranslationUnit} + * and its nested {@code IASTProblem}. */ public static final ASTNodeProperty SCANNER_PROBLEM = new ASTNodeProperty( "IASTTranslationUnit.SCANNER_PROBLEM - IASTProblem (scanner caused) for IASTTranslationUnit"); //$NON-NLS-1$ /** - * PREPROCESSOR_STATEMENT represents the relationship between an IASTTranslationUnit and - * it's nested IASTPreprocessorStatement. + * {@code PREPROCESSOR_STATEMENT} represents the relationship between + * an {@code IASTTranslationUnit} and its nested {@code IASTPreprocessorStatement}. */ public static final ASTNodeProperty PREPROCESSOR_STATEMENT = new ASTNodeProperty( "IASTTranslationUnit.PREPROCESSOR_STATEMENT - IASTPreprocessorStatement for IASTTranslationUnit"); //$NON-NLS-1$ + public static final ASTNodeProperty MACRO_EXPANSION = new ASTNodeProperty( + "IASTTranslationUnit.MACRO_EXPANSION - IASTPreprocessorMacroExpansion node for macro expansions."); //$NON-NLS-1$ + + /** + * @deprecated names for macro expansions are nested inside of + * {@link IASTPreprocessorMacroExpansion}. + */ + @Deprecated + public static final ASTNodeProperty EXPANSION_NAME = new ASTNodeProperty( + "IASTTranslationUnit.EXPANSION_NAME - IASTName generated for macro expansions."); //$NON-NLS-1$ + /** * A translation unit contains an ordered sequence of declarations. * @@ -62,7 +73,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi /** * Adds declaration to translation unit. * - * @param declaration IASTDeclaration + * @param declaration {@code IASTDeclaration} */ @Override public void addDeclaration(IASTDeclaration declaration); @@ -130,7 +141,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi * The object is suitable for working in one of the files that is part of * the translation unit. * @param filePath file of interest, as returned by {@link IASTFileLocation#getFileName()}, - * or null to specify the root source of the translation-unit. + * or {@code null} to specify the root source of the translation-unit. * @return an IASTNodeSelector. * @since 5.0 */ @@ -158,8 +169,8 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi public IASTPreprocessorMacroDefinition[] getBuiltinMacroDefinitions(); /** - * Returns the include directives encountered in parsing this translation unit. This will also contain directives - * used for handling the gcc-options -imacros and -include. + * Returns the include directives encountered in parsing this translation unit. This will also + * contain directives used for handling the gcc-options -imacros and -include. *

* In case the information for a header-file is pulled in from the index, * include directives contained therein are not returned. @@ -180,7 +191,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi /** * Returns all preprocessor and scanner problems. - * @return IASTProblem[] + * @return {@code IASTProblem[]} */ public IASTProblem[] getPreprocessorProblems(); @@ -199,21 +210,11 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi /** * Flattens the node locations provided into a single file location. * - * @param nodeLocations IASTNodeLocations to flatten + * @param nodeLocations {@code IASTNodeLocation}s to flatten * @return null if not possible, otherwise, a file location representing where the macros are. */ public IASTFileLocation flattenLocationsToFile(IASTNodeLocation[] nodeLocations); - /** - * @deprecated names for macro expansions are nested inside of {@link IASTPreprocessorMacroExpansion}. - */ - @Deprecated - public static final ASTNodeProperty EXPANSION_NAME = new ASTNodeProperty( - "IASTTranslationUnit.EXPANSION_NAME - IASTName generated for macro expansions."); //$NON-NLS-1$ - - public static final ASTNodeProperty MACRO_EXPANSION = new ASTNodeProperty( - "IASTTranslationUnit.MACRO_EXPANSION - IASTPreprocessorMacroExpansion node for macro expansions."); //$NON-NLS-1$ - public static interface IDependencyTree { public String getTranslationUnitPath(); @@ -253,7 +254,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi /** * Return the set of files that have been skipped because they have been part of the index - * prior to creating this AST, or null if not available. + * prior to creating this AST, or {@code null} if not available. * Applies only, if AST was created with an index and the option to skip headers found in * the index. * @since 5.1 @@ -261,7 +262,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi IIndexFileSet getIndexFileSet(); /** - * Return the set of files in the index that are superseded by this AST, or null + * Return the set of files in the index that are superseded by this AST, or {@code null} * if not available. Applies only, if AST was created with an index. * @since 5.3 */ @@ -271,7 +272,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi * In case the AST was created in a way that supports comment parsing, all comments of * the translation unit are returned. Otherwise an empty array will be supplied. * - * @return IASTComment[] + * @return {@code IASTComment[]} * @since 4.0 */ public IASTComment[] getComments(); @@ -339,7 +340,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi public IASTTranslationUnit copy(CopyStyle style); /** - * Returns the ITranslationUnit this AST originated from, or null if the AST + * Returns the ITranslationUnit this AST originated from, or {@code null} if the AST * does not correspond to an ITranslationUnit. * * @since 5.3 diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTForStatement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTForStatement.java index ae47d0d8b67..1b81285abbc 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTForStatement.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTForStatement.java @@ -15,14 +15,16 @@ import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTForStatement; /** - * + * The C++ 'for' statement. + * * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. */ public interface ICPPASTForStatement extends IASTForStatement { - - public static final ASTNodeProperty CONDITION_DECLARATION = new ASTNodeProperty( "org.eclipse.cdt.core.dom.ast.cpp.ICPPASTForStatement"); //$NON-NLS-1$ - public void setConditionDeclaration( IASTDeclaration d ); + public static final ASTNodeProperty CONDITION_DECLARATION = + new ASTNodeProperty("org.eclipse.cdt.core.dom.ast.cpp.ICPPASTForStatement"); //$NON-NLS-1$ + + public void setConditionDeclaration(IASTDeclaration d); public IASTDeclaration getConditionDeclaration(); /** diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPEnumeration.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPEnumeration.java index 22225a116aa..b3bff980948 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPEnumeration.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPEnumeration.java @@ -28,7 +28,7 @@ public interface ICPPEnumeration extends IEnumeration, ICPPBinding { boolean isScoped(); /** - * Returns the underlying type of the enumeration if it is fixed, or null otherwise. + * Returns the underlying type of the enumeration if it is fixed, or {@code null} otherwise. * The underlying type can only be fixed in C++. */ IType getFixedType(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java index 330878bb36c..6307942c555 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java @@ -74,6 +74,21 @@ public class CharArrayUtils { return true; } + /** + * Returns {@code true} if the contents of a section of a character array are the same as + * contents of a string. + * @since 5.5 + */ + public static final boolean equals(char[] str1, int start1, int length1, String str2) { + if (length1 != str2.length() || str1.length < length1 + start1) + return false; + for (int i = 0; i < length1; ++i) { + if (str1[start1++] != str2.charAt(i)) + return false; + } + return true; + } + /** * Returns {@code true} if a prefix of the character array is the same as contents * of a string. @@ -117,6 +132,10 @@ public class CharArrayUtils { return str1.length - str2.length; } + /** + * Returns {@code true} if the contents of a section of a character array are the same as + * contents of another character array. + */ public static final boolean equals(char[] str1, int start1, int length1, char[] str2) { if (length1 != str2.length || str1.length < length1 + start1) return false; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java index 17e83bb272a..b6b9281b02c 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java @@ -485,4 +485,52 @@ public abstract class ASTTranslationUnit extends ASTNode implements IASTTranslat } return fSizeofCalculator; } + + /** + * Returns the offset of the given node, or -1 if the node is not part of the translation + * unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getNodeOffset(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return -1; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getNodeOffset() : -1; + } + + /** + * Returns the end offset of the given node, or -1 if the node is not part of the translation + * unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getNodeEndOffset(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return -1; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getNodeOffset() + nodeLocation.getNodeLength() : -1; + } + + /** + * Returns the 1-based starting line number of the given node, or 0 if the node is not part of + * the translation unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getStartingLineNumber(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return 0; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getStartingLineNumber() : 0; + } + + /** + * Returns the 1-based ending line number of the given node, or 0 if the node is not part of + * the translation unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getEndingLineNumber(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return 0; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getEndingLineNumber() : 0; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTLinkageSpecification.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTLinkageSpecification.java index 7a5b2a3c690..370ef687b8d 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTLinkageSpecification.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTLinkageSpecification.java @@ -6,7 +6,7 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * John Camelon (IBM) - Initial API and implementation + * John Camelon (IBM) - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp; @@ -22,13 +22,12 @@ import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent; /** * Extern "C" construct. */ -public class CPPASTLinkageSpecification extends ASTNode implements - ICPPASTLinkageSpecification, IASTAmbiguityParent { - +public class CPPASTLinkageSpecification extends ASTNode + implements ICPPASTLinkageSpecification, IASTAmbiguityParent { private String fLiteral; private IASTDeclaration[] fAllDeclarations; private IASTDeclaration[] fActiveDeclarations; - private int fLastDeclaration=-1; + private int fLastDeclaration = -1; public CPPASTLinkageSpecification() { } @@ -45,13 +44,10 @@ public class CPPASTLinkageSpecification extends ASTNode implements @Override public CPPASTLinkageSpecification copy(CopyStyle style) { CPPASTLinkageSpecification copy = new CPPASTLinkageSpecification(fLiteral); - for (IASTDeclaration declaration : getDeclarations()) + for (IASTDeclaration declaration : getDeclarations()) { copy.addDeclaration(declaration == null ? null : declaration.copy(style)); - copy.setOffsetAndLength(this); - if (style == CopyStyle.withLocations) { - copy.setCopyLocation(this); } - return copy; + return copy(copy, style); } @Override @@ -70,7 +66,7 @@ public class CPPASTLinkageSpecification extends ASTNode implements if (decl != null) { decl.setParent(this); decl.setPropertyInParent(OWNED_DECLARATION); - fAllDeclarations = ArrayUtil.appendAt( IASTDeclaration.class, fAllDeclarations, ++fLastDeclaration, decl); + fAllDeclarations = ArrayUtil.appendAt(IASTDeclaration.class, fAllDeclarations, ++fLastDeclaration, decl); fActiveDeclarations= null; } } @@ -99,9 +95,9 @@ public class CPPASTLinkageSpecification extends ASTNode implements public boolean accept(ASTVisitor action) { if (action.shouldVisitDeclarations) { switch (action.visit(this)) { - case ASTVisitor.PROCESS_ABORT : return false; - case ASTVisitor.PROCESS_SKIP : return true; - default : break; + case ASTVisitor.PROCESS_ABORT: return false; + case ASTVisitor.PROCESS_SKIP: return true; + default: break; } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java index 30dc0f59346..f78c870b83f 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2009 Institute for Software, HSR Hochschule fuer Technik + * Copyright (c) 2008, 2013 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -7,7 +7,8 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Institute for Software - initial API and implementation + * Institute for Software - initial API and implementation + * Sergey Prigogin (Google) ******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite.commenthandler; @@ -64,7 +65,7 @@ public class ASTCommenter { } private int checkOffsets(IASTNode node) { - int offset = ((ASTNode)node).getOffset(); + int offset = ((ASTNode) node).getOffset(); int status = PROCESS_CONTINUE; if (isCommentOnSameLine(node) @@ -82,8 +83,8 @@ public class ASTCommenter { } private boolean isCommentOnSameLine(IASTNode node) { - return commentNodeLocation.getStartingLineNumber() == node.getFileLocation() - .getEndingLineNumber(); + return commentNodeLocation.getStartingLineNumber() == + node.getFileLocation().getEndingLineNumber(); } @Override @@ -168,41 +169,29 @@ public class ASTCommenter { } /** - * Creates a NodeCommentMap for the given TranslationUnit. This is the only way - * to get a NodeCommentMap which contains all the comments mapped against nodes. + * Creates a NodeCommentMap for the given AST. This is the only way to get a NodeCommentMap + * which contains all the comments mapped against nodes. * - * @param tu TranslationUnit + * @param ast the AST * @return NodeCommentMap */ - public static NodeCommentMap getCommentedNodeMap(IASTTranslationUnit tu){ + public static NodeCommentMap getCommentedNodeMap(IASTTranslationUnit ast) { NodeCommentMap commentMap = new NodeCommentMap(); - if (tu == null) { + if (ast == null) { return commentMap; } - IASTComment[] commentsArray = tu.getComments(); - if (commentsArray == null) { - return commentMap; - } - // Note that constructing a real ArrayList is required here, since in filterNonTuComments, the - // remove-method will be invoked on the list's iterator. Calling it on the type Arrays$ArrayList (the - // resulting type of Arrays.asList() ) would throw a UnsupportedOperationException. - ArrayList comments = new ArrayList(Arrays.asList(commentsArray)); - filterNonTuComments(comments); - return addCommentsToCommentMap(tu, comments); - } - - /** - * Note that passing an ArrayList (instead of just List or Collection) is required here, since this - * guarantees that the call to the remove-method on the list's iterator will not result in an - * UnsupportedOperationException which might be the case for other Collection/List types. - */ - private static void filterNonTuComments(ArrayList comments) { - Iterator iterator = comments.iterator(); - while (iterator.hasNext()) { - if (!iterator.next().isPartOfTranslationUnitFile()) { - iterator.remove(); + IASTComment[] commentsArray = ast.getComments(); + List comments = new ArrayList(commentsArray.length); + for (IASTComment comment : commentsArray) { + if (comment.isPartOfTranslationUnitFile()) { + comments.add(comment); } } + assignPreprocessorComments(commentMap, comments, ast); + CommentHandler commentHandler = new CommentHandler(comments); + ASTCommenterVisitor commenter = new ASTCommenterVisitor(commentHandler, commentMap); + ast.accept(commenter); + return commentMap; } private static boolean isCommentDirectlyBeforePreprocessorStatement(IASTComment comment, @@ -211,11 +200,11 @@ public class ASTCommenter { return true; } IASTFileLocation commentLocation = comment.getFileLocation(); - int preprcessorOffset = statement.getFileLocation().getNodeOffset(); - if (preprcessorOffset > commentLocation.getNodeOffset()) { - PreprocessorRangeChecker vister = new PreprocessorRangeChecker(preprcessorOffset, commentLocation); - tu.accept(vister); - return vister.isPreStatementComment; + int preprocessorOffset = statement.getFileLocation().getNodeOffset(); + if (preprocessorOffset > commentLocation.getNodeOffset()) { + PreprocessorRangeChecker visitor = new PreprocessorRangeChecker(preprocessorOffset, commentLocation); + tu.accept(visitor); + return visitor.isPreStatementComment; } return false; } @@ -224,24 +213,12 @@ public class ASTCommenter { return node.isPartOfTranslationUnitFile(); } - private static NodeCommentMap addCommentsToCommentMap(IASTTranslationUnit tu, - ArrayList comments){ - NodeCommentMap commentMap = new NodeCommentMap(); - CommentHandler commHandler = new CommentHandler(comments); - - assignPreprocessorComments(commentMap, comments, tu); - ASTCommenterVisitor commenter = new ASTCommenterVisitor(commHandler, commentMap); - tu.accept(commenter); - return commentMap; - } - /** - * Note that passing an ArrayList (instead of just List or Collection) is required here, since this - * guarantees that the call to the remove-method on the list's iterator will not result in an - * UnsupportedOperationException which might be the case for other Collection/List types. + * Puts leading and trailing comments to {@code commentMap} and removes them from + * the {@code comments} list. */ private static void assignPreprocessorComments(NodeCommentMap commentMap, - ArrayList comments, IASTTranslationUnit tu) { + List comments, IASTTranslationUnit tu) { IASTPreprocessorStatement[] preprocessorStatementsArray = tu.getAllPreprocessorStatements(); if (preprocessorStatementsArray == null) { return; @@ -252,6 +229,7 @@ public class ASTCommenter { return; } + List freestandingComments = new ArrayList(comments.size()); Iterator statementsIter = preprocessorStatements.iterator(); Iterator commentIter = comments.iterator(); IASTPreprocessorStatement curStatement = getNextNodeInTu(statementsIter); @@ -261,18 +239,26 @@ public class ASTCommenter { int commentLineNr = curComment.getFileLocation().getStartingLineNumber(); if (commentLineNr == statementLineNr) { commentMap.addTrailingCommentToNode(curStatement, curComment); - commentIter.remove(); curComment = getNextNodeInTu(commentIter); } else if (commentLineNr > statementLineNr) { curStatement = getNextNodeInTu(statementsIter); } else if (isCommentDirectlyBeforePreprocessorStatement(curComment, curStatement, tu)) { commentMap.addLeadingCommentToNode(curStatement, curComment); - commentIter.remove(); curComment = getNextNodeInTu(commentIter); } else { + freestandingComments.add(curComment); curComment = getNextNodeInTu(commentIter); } } + while (curComment != null) { + freestandingComments.add(curComment); + curComment = getNextNodeInTu(commentIter); + } + + if (freestandingComments.size() != comments.size()) { + comments.clear(); + comments.addAll(freestandingComments); + } } private static T getNextNodeInTu(Iterator iter) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java index a8a97e9dbb8..030b99f16d5 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java @@ -39,7 +39,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTCompositeTypeSpecifier * @author Guido Zgraggen IFS */ public class ASTCommenterVisitor extends ASTVisitor { - protected CommentHandler commHandler; + protected CommentHandler commentHandler; protected NodeCommentMap commentMap; private NodeCommenter nodeCommenter; @@ -61,14 +61,14 @@ public class ASTCommenterVisitor extends ASTVisitor { shouldVisitTranslationUnit = true; } - public ASTCommenterVisitor(CommentHandler commHandler, NodeCommentMap commentMap) { - this.commHandler = commHandler; + public ASTCommenterVisitor(CommentHandler commentHandler, NodeCommentMap commentMap) { + this.commentHandler = commentHandler; this.commentMap = commentMap; init(); } private void init() { - nodeCommenter = new NodeCommenter(this, commHandler, commentMap); + nodeCommenter = new NodeCommenter(this, commentHandler, commentMap); } @Override diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java index ec0d9f9afc8..589a67b2510 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java @@ -62,13 +62,13 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTWhileStatement; */ public class NodeCommenter { protected ASTVisitor visitor; - protected CommentHandler commHandler; + protected CommentHandler commentHandler; protected NodeCommentMap commentMap; protected List children; public NodeCommenter(ASTVisitor visitor, CommentHandler commHandler, NodeCommentMap commentMap) { this.visitor = visitor; - this.commHandler = commHandler; + this.commentHandler = commHandler; this.commentMap = commentMap; this.children = new ArrayList(); } @@ -122,17 +122,17 @@ public class NodeCommenter { private void addLeadingCommentToMap(ASTNode node, IASTComment comment) { commentMap.addLeadingCommentToNode(node, comment); - commHandler.allreadyAdded(comment); + commentHandler.allreadyAdded(comment); } private void addTrailingCommentToMap(ASTNode node, IASTComment comment) { commentMap.addTrailingCommentToNode(node, comment); - commHandler.allreadyAdded(comment); + commentHandler.allreadyAdded(comment); } private void addFreestandingCommentToMap(ASTNode node, IASTComment comment) { commentMap.addFreestandingCommentToNode(node, comment); - commHandler.allreadyAdded(comment); + commentHandler.allreadyAdded(comment); } private boolean isTrailing(ASTNode node, ASTNode com, int nodeLineNumber, int commentLineNumber) { @@ -200,8 +200,8 @@ public class NodeCommenter { } protected int appendComments(ASTNode node) { - while (commHandler.hasMore()) { - IASTComment comment = commHandler.getFirst(); + while (commentHandler.hasMore()) { + IASTComment comment = commentHandler.getFirst(); if (isNotSameFile(node, comment)) { return ASTVisitor.PROCESS_SKIP; @@ -215,8 +215,8 @@ public class NodeCommenter { } protected int appendFreestandingComments(ASTNode node) { - while (commHandler.hasMore()) { - IASTComment comment = commHandler.getFirst(); + while (commentHandler.hasMore()) { + IASTComment comment = commentHandler.getFirst(); if (isNotSameFile(node, comment)) { return ASTVisitor.PROCESS_SKIP; @@ -234,9 +234,9 @@ public class NodeCommenter { } public void appendRemainingComments(IASTDeclaration declaration) { - while (commHandler.hasMore()) { - IASTComment comment = commHandler.getFirst(); - if (appendComment((ASTNode)declaration, comment)) { + while (commentHandler.hasMore()) { + IASTComment comment = commentHandler.getFirst(); + if (appendComment((ASTNode) declaration, comment)) { continue; } addFreestandingCommentToMap((ASTNode) declaration, comment); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java index 43bc89e7012..993acd2402a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java @@ -113,6 +113,7 @@ abstract class ASTPreprocessorNode extends ASTNode { class ASTComment extends ASTPreprocessorNode implements IASTComment { private final boolean fIsBlockComment; private String fFilePath; + public ASTComment(IASTTranslationUnit parent, String filePath, int offset, int endOffset, boolean isBlockComment) { super(parent, IASTTranslationUnit.PREPROCESSOR_STATEMENT, offset, endOffset); fIsBlockComment= isBlockComment; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java index 66382144eba..6d6518d4200 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java @@ -68,8 +68,8 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; /** - * C-Preprocessor providing tokens for the parsers. The class should not be used directly, rather than that - * you should be using the {@link IScanner} interface. + * C-Preprocessor providing tokens for the parsers. The class should not be used directly, + * rather than that you should be using the {@link IScanner} interface. * @since 5.0 */ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { @@ -87,8 +87,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { private static final char[] ONE = "1".toCharArray(); //$NON-NLS-1$ - - // standard built-ins + // Standard built-ins private static final ObjectStyleMacro __CDT_PARSER__= new ObjectStyleMacro("__CDT_PARSER__".toCharArray(), ONE); //$NON-NLS-1$ private static final ObjectStyleMacro __cplusplus = new ObjectStyleMacro("__cplusplus".toCharArray(), ONE); //$NON-NLS-1$ private static final ObjectStyleMacro __STDC__ = new ObjectStyleMacro("__STDC__".toCharArray(), ONE); //$NON-NLS-1$ @@ -374,7 +373,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { return fLocationMap; } - private void configureKeywords(ParserLanguage language, IScannerExtensionConfiguration configuration) { + private void configureKeywords(ParserLanguage language, IScannerExtensionConfiguration configuration) { Keywords.addKeywordsPreprocessor(fPPKeywords); if (language == ParserLanguage.C) { Keywords.addKeywordsC(fKeywords); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeGuardDetection.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeGuardDetection.java index 2674e80e36a..cdd034e83f8 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeGuardDetection.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeGuardDetection.java @@ -6,9 +6,8 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Markus Schorn - initial API and implementation + * Markus Schorn - initial API and implementation *******************************************************************************/ - package org.eclipse.cdt.internal.core.parser.scanner; import static org.eclipse.cdt.core.parser.OffsetLimitReachedException.ORIGIN_PREPROCESSOR_DIRECTIVE; @@ -39,7 +38,7 @@ public class IncludeGuardDetection { Token t = l.nextToken(); if (t.getType() == IToken.tIDENTIFIER) { char[] guard= null; - switch(ppKeywords.get(t.getCharImage())) { + switch (ppKeywords.get(t.getCharImage())) { case IPreprocessorDirective.ppIfndef: // #ifndef GUARD t= l.nextToken(); @@ -98,7 +97,7 @@ public class IncludeGuardDetection { Token t= l.nextDirective(); if (t.getType() == IToken.tEND_OF_INPUT) return true; - switch(ppKeywords.get(l.nextToken().getCharImage())) { + switch (ppKeywords.get(l.nextToken().getCharImage())) { case IPreprocessorDirective.ppIf: case IPreprocessorDirective.ppIfdef: case IPreprocessorDirective.ppIfndef: @@ -124,7 +123,6 @@ public class IncludeGuardDetection { return t; } - public static boolean detectIncludeEndif(Lexer l) { l.saveState(); try { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java index 458dbc3f632..8c397858cf4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java @@ -7,11 +7,16 @@ * * Contributors: * Markus Schorn - initial API and implementation + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.parser.scanner; import java.io.File; +import org.eclipse.cdt.utils.PathUtil; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + /** * Represents an entry of the include search path */ @@ -42,14 +47,14 @@ public final class IncludeSearchPathElement { public String getLocation(String includeDirective) { if (fIsFrameworkDirectory) { int firstSep = firstSeparator(includeDirective); - if (firstSep < 1) { + if (firstSep <= 0) { return null; } String framework = includeDirective.substring(0, firstSep); String file= includeDirective.substring(firstSep + 1); if (file.length() == 0) return null; - + StringBuilder buf= new StringBuilder(fPath); replace(buf, FRAMEWORK_VAR, framework); replace(buf, FILE_VAR, file); @@ -58,6 +63,42 @@ public final class IncludeSearchPathElement { return ScannerUtility.createReconciledPath(fPath, includeDirective); } + /** + * Returns the include directive for the given location satisfying the condition + * {@code getLocation(getIncludeDirective(location) == location}. If no such include directive + * without ".." exists, returns {@code null}. + */ + public String getIncludeDirective(String location) { + IPath dirPath = new Path(fPath); + IPath locationPath = new Path(location); + if (fIsFrameworkDirectory) { + if (dirPath.segmentCount() != locationPath.segmentCount()) + return null; + int i = PathUtil.matchingFirstSegments(dirPath, locationPath); + String dirSegment = dirPath.segment(i); + String locationSegment = locationPath.segment(i); + String framework = deduceVariable(FRAMEWORK_VAR, dirSegment, locationSegment); + if (framework == null) + return null; + i++; + dirPath = dirPath.removeFirstSegments(i); + locationPath = locationPath.removeFirstSegments(i); + i = PathUtil.matchingFirstSegments(dirPath, locationPath); + if (i < dirPath.segmentCount() - 1) + return null; + dirSegment = dirPath.segment(i); + locationSegment = locationPath.segment(i); + String file = deduceVariable(FILE_VAR, dirSegment, locationSegment); + if (file == null) + return null; + return framework + '/' + file; + } + + if (!PathUtil.isPrefix(dirPath, locationPath)) + return null; + return locationPath.removeFirstSegments(dirPath.segmentCount()).setDevice(null).toPortableString(); + } + private int firstSeparator(String path) { int firstSep= path.indexOf('/'); if (NON_SLASH_SEPARATOR) { @@ -73,6 +114,20 @@ public final class IncludeSearchPathElement { } } + private String deduceVariable(String varName, String raw, String substituted) { + int pos = raw.indexOf(varName); + if (pos < 0) + return null; + int suffixLength = raw.length() - pos - varName.length(); + if (substituted.length() <= pos + suffixLength) + return null; + for (int i = 0; i < suffixLength; i++) { + if (raw.charAt(raw.length() - i) != substituted.charAt(substituted.length() - i)) + return null; + } + return substituted.substring(pos, substituted.length() - suffixLength); + } + /** * For debugging only. */ diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java index e1836964b4f..70109e01c1b 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 Google, Inc 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 @@ -22,7 +22,6 @@ import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.TextSelection; @@ -56,8 +55,6 @@ import org.eclipse.cdt.internal.ui.refactoring.CRefactoringContext; * Common base for refactoring tests. */ public abstract class RefactoringTestBase extends BaseTestCase { - protected static final NullProgressMonitor NULL_PROGRESS_MONITOR = new NullProgressMonitor(); - /** Allows empty files to be created during test setup. */ protected boolean createEmptyFiles = true; /** See {@link PreferenceConstants.CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER} */ @@ -137,7 +134,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { public void tearDown() throws Exception { if (cproject != null) { cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, - NULL_PROGRESS_MONITOR); + npm()); } resetPreferences(); super.tearDown(); @@ -175,7 +172,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { protected void executeRefactoring(Refactoring refactoring, RefactoringContext context, boolean withUserInput, boolean expectedSuccess) throws CoreException, Exception { try { - RefactoringStatus initialStatus = refactoring.checkInitialConditions(NULL_PROGRESS_MONITOR); + RefactoringStatus initialStatus = refactoring.checkInitialConditions(npm()); if (!expectedSuccess) { assertStatusFatalError(initialStatus); return; @@ -190,7 +187,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { if (withUserInput) simulateUserInput(); - RefactoringStatus finalStatus = refactoring.checkFinalConditions(NULL_PROGRESS_MONITOR); + RefactoringStatus finalStatus = refactoring.checkFinalConditions(npm()); if (expectedFinalWarnings != 0) { assertStatusWarning(finalStatus, expectedFinalWarnings); } else if (expectedFinalInfos != 0) { @@ -198,8 +195,8 @@ public abstract class RefactoringTestBase extends BaseTestCase { } else { assertStatusOk(finalStatus); } - Change change = refactoring.createChange(NULL_PROGRESS_MONITOR); - change.perform(NULL_PROGRESS_MONITOR); + Change change = refactoring.createChange(npm()); + change.perform(npm()); } finally { if (context != null) context.dispose(); @@ -212,7 +209,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { RefactoringHistory history = RefactoringHistoryService.getInstance().readRefactoringHistory( new ByteArrayInputStream(scriptSource.getBytes()), 0); for (RefactoringDescriptorProxy proxy : history.getDescriptors()) { - RefactoringDescriptor descriptor = proxy.requestDescriptor(NULL_PROGRESS_MONITOR); + RefactoringDescriptor descriptor = proxy.requestDescriptor(npm()); RefactoringStatus status = new RefactoringStatus(); RefactoringContext context = descriptor.createRefactoringContext(status); assertTrue(status.isOK()); diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java index 3969debfd10..496e5c0bca4 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java @@ -22,6 +22,7 @@ import org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVar import org.eclipse.cdt.ui.tests.refactoring.gettersandsetters.GenerateGettersAndSettersTest; import org.eclipse.cdt.ui.tests.refactoring.hidemethod.HideMethodRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest; +import org.eclipse.cdt.ui.tests.refactoring.includes.IncludesTestSuite; import org.eclipse.cdt.ui.tests.refactoring.rename.RenameRegressionTests; import org.eclipse.cdt.ui.tests.refactoring.togglefunction.ToggleRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.utils.UtilTestSuite; @@ -42,6 +43,7 @@ public class RefactoringTestSuite extends TestSuite { suite.addTest(ImplementMethodRefactoringTest.suite()); suite.addTest(ExtractLocalVariableRefactoringTest.suite()); suite.addTest(ToggleRefactoringTest.suite()); + suite.addTest(IncludesTestSuite.suite()); return suite; } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java index 4446a6de752..76a7edcea93 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java @@ -1,11 +1,11 @@ /******************************************************************************* * Rapperswil, University of applied sciences 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 - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: + * 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: * Institute for Software - initial API and implementation * Sergey Prigogin (Google) *******************************************************************************/ @@ -181,7 +181,6 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { assertRefactoringSuccess(); } - //A.h //#ifndef A_H_ //#define A_H_ @@ -308,7 +307,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { public void testLocalVariableDeclaration_3() throws Exception { assertRefactoringSuccess(); } - + //A.h //#ifndef A_H_ //#define A_H_ diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java new file mode 100644 index 00000000000..c9329d20606 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2012, 2013 Google, Inc 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.includes; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import junit.framework.TestSuite; + +import com.ibm.icu.text.MessageFormat; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexManager; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.testplugin.util.OneSourceMultipleHeadersTestCase; +import org.eclipse.cdt.core.testplugin.util.TestSourceReader; +import org.eclipse.cdt.ui.testplugin.CTestPlugin; + +import org.eclipse.cdt.internal.ui.refactoring.includes.BindingClassifier; +import org.eclipse.cdt.internal.ui.refactoring.includes.InclusionContext; + +public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { + private IIndex fIndex; + private InclusionContext fContext; + private BindingClassifier fBindingClassifier; + + public BindingClassifierTest() { + super(new TestSourceReader(CTestPlugin.getDefault().getBundle(), "ui", BindingClassifierTest.class), true); + } + + public static TestSuite suite() { + return suite(BindingClassifierTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + IASTTranslationUnit ast = getAst(); + fIndex = CCorePlugin.getIndexManager().getIndex(getCProject(), + IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT); + fIndex.acquireReadLock(); + ITranslationUnit tu = ast.getOriginatingTranslationUnit(); + fContext = new InclusionContext(tu, fIndex); + fBindingClassifier = new BindingClassifier(fContext); + fBindingClassifier.classifyNodeContents(ast); + } + + @Override + protected void tearDown() throws Exception { + fIndex.releaseReadLock(); + super.tearDown(); + } + + private void assertDefined(String... names) { + assertExpectedBindings(names, fBindingClassifier.getBindingsToDefine(), "defined"); + } + + private void assertDeclared(String... names) { + assertExpectedBindings(names, fBindingClassifier.getBindingsToDeclare(), "declared"); + } + + private void assertExpectedBindings(String[] expectedNames, Set bindings, String verb) { + Set expected = new TreeSet(Arrays.asList(expectedNames)); + Set extra = new TreeSet(); + for (IBinding binding : bindings) { + extra.add(binding.getName()); + } + Set missing = new TreeSet(expected); + missing.removeAll(extra); + extra.removeAll(expected); + if (extra.isEmpty() && missing.isEmpty()) + return; + List errors = new ArrayList(2); + if (!missing.isEmpty()) { + errors.add(MessageFormat.format("{0,choice,1#Binding|1 strings, String delimiter) { + StringBuilder buf = new StringBuilder(); + for (String str : strings) { + if (buf.length() != 0) + buf.append(delimiter); + buf.append(str); + } + return buf.toString(); + } + + // class A; + // typedef A* td1; + // typedef td1* td2; + // td2 f(); + + // A* a = *f(); + public void testTypedef_1() throws Exception { + assertDefined("f"); + assertDeclared("A"); + } + + // class A; + // typedef A* td1; + // typedef td1* td2; + // td2 f(); + + // td1 a = *f(); + public void testTypedef_2() throws Exception { + assertDefined("f", "td1"); + } + + // class A { int x; }; + // typedef A* td; + // td f(); + + // int a = f()->x; + public void testClassMember() throws Exception { + assertDefined("f", "A"); + } + + // class A { void m(); }; + + // void test(A* a) { + // a->m(); + // } + public void testMethodCall() throws Exception { + assertDefined("A"); + } + + // struct A {}; + // struct B {}; + + // struct C { + // A a; + // static B b; + // }; + public void testFieldReference() throws Exception { + assertDefined("A"); + assertDeclared("B"); + } + + // int a; + + // void test() { + // void* x = &a; + // } + public void testVariableReference() throws Exception { + assertDefined("a"); // Forward declaration of variables is not allowed by default. + } + + // struct A { + // void operator()(int p); + // }; + // const A a; + + // void test() { + // a(1); + // } + public void testCallOperator() throws Exception { + assertDefined("A", "a"); // Forward declaration of variables is not allowed by default. + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java new file mode 100644 index 00000000000..e4594327976 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.includes; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.preference.IPreferenceStore; +import org.osgi.framework.Bundle; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.IPDOMManager; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.core.testplugin.util.TestSourceReader; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.testplugin.CTestPlugin; +import org.eclipse.cdt.ui.tests.refactoring.TestSourceFile; + +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; + +/** + * Common base for include-related tests. + */ +public abstract class IncludesTestBase extends BaseTestCase { + protected final String LINE_DELIMITER = "\n"; + + protected static class FirstHeaderChooser implements IHeaderChooser { + @Override + public IPath chooseHeader(String bindingName, Collection headers) { + return headers.isEmpty() ? null : headers.iterator().next(); + } + } + + /** Expected counts of errors, warnings and info messages */ + protected int expectedInitialErrors; + protected int expectedInitialWarnings; + protected int expectedFinalWarnings; + protected int expectedFinalInfos; + + protected IIndex index; + protected ICProject cproject; + protected IASTTranslationUnit ast; + protected TestSourceFile selectedFile; + private StringBuilder[] testData; + private boolean cpp = true; + private final Set testFiles = new LinkedHashSet(); + + protected IncludesTestBase() { + super(); + } + + protected IncludesTestBase(String name) { + super(name); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + resetPreferences(); + cproject = cpp ? + CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) : + CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER); + Bundle bundle = CTestPlugin.getDefault().getBundle(); + CharSequence[] testData = TestSourceReader.getContentsForTest(bundle, "ui", getClass(), getName(), 0); + + IFile sourceFile = null; + for (CharSequence contents : testData) { + TestSourceFile testFile = null; + boolean expectedResult = false; + BufferedReader reader = new BufferedReader(new StringReader(contents.toString())); + String line; + while ((line = reader.readLine()) != null) { + String trimmedLine = line.trim(); + if (testFile == null) { + assertTrue("Invalid file name \"" + trimmedLine + "\"", trimmedLine.matches("^(\\w+/)*\\w+\\.\\w+$")); + testFile = new TestSourceFile(trimmedLine); + } else if (isResultDelimiter(trimmedLine)) { + expectedResult = true; + } else if (expectedResult) { + testFile.addLineToExpectedSource(line); + } else { + testFile.addLineToSource(line); + } + } + reader.close(); + + sourceFile = TestSourceReader.createFile(cproject.getProject(), new Path(testFile.getName()), + testFile.getSource()); + testFiles.add(testFile); + selectedFile = testFile; + } + CCorePlugin.getIndexManager().setIndexerId(cproject, IPDOMManager.ID_FAST_INDEXER); + waitForIndexer(cproject); + + index= CCorePlugin.getIndexManager().getIndex(cproject); + + index.acquireReadLock(); + ast = TestSourceReader.createIndexBasedAST(index, cproject, sourceFile); + } + + @Override + public void tearDown() throws Exception { + if (cproject != null) { + cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, npm()); + } + resetPreferences(); + super.tearDown(); + } + + protected ICProject getCProject() { + return cproject; + } + + protected TestSourceFile getSelectedTestFile() { + return selectedFile; + } + + protected IFile getSelectedFile() { + if (selectedFile == null) + return null; + return cproject.getProject().getFile(new Path(selectedFile.getName())); + } + + protected ITranslationUnit getSelectedTranslationUnit() { + IFile file = getSelectedFile(); + if (file == null) + return null; + return (ITranslationUnit) CoreModel.getDefault().create(file); + } + + protected boolean isCpp() { + return cpp; + } + + protected void setCpp(boolean cpp) { + this.cpp = cpp; + } + + private boolean isResultDelimiter(String str) { + if (str.isEmpty()) + return false; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) != '=') + return false; + } + return true; + } + + protected void resetPreferences() { + } + + protected IPreferenceStore getPreferenceStore() { + return CUIPlugin.getDefault().getPreferenceStore(); + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java new file mode 100644 index 00000000000..cb49b4da418 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.includes; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class IncludesTestSuite extends TestSuite { + + public static Test suite() throws Exception { + IncludesTestSuite suite = new IncludesTestSuite(); + suite.addTest(BindingClassifierTest.suite()); + suite.addTest(OrganizeIncludesTest.suite()); + return suite; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java new file mode 100644 index 00000000000..50fa7d63d68 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java @@ -0,0 +1,298 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.includes; + +import java.util.List; + +import junit.framework.Test; + +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.TextEdit; + +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.PreferenceConstants; + +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer; + +/** + * Tests for Extract Function refactoring. + */ +public class OrganizeIncludesTest extends IncludesTestBase { + + public OrganizeIncludesTest() { + super(); + } + + public OrganizeIncludesTest(String name) { + super(name); + } + + public static Test suite() { + return suite(OrganizeIncludesTest.class); + } + + @Override + protected void resetPreferences() { + super.resetPreferences(); + getPreferenceStore().setToDefault(PreferenceConstants.INCLUDES_UNUSED_STATEMENTS_DISPOSITION); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_COMPOSITE_TYPES); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_ENUMS); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_TEMPLATES); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_NAMESPACE_ELEMENTS); + getPreferenceStore().setToDefault(PreferenceConstants.INCLUDES_ALLOW_REORDERING); + } + + private void assertExpectedResults() throws Exception { + String actual = organizeIncludes(ast.getOriginatingTranslationUnit()); + assertEquals(selectedFile.getExpectedSource(), actual); + } + + /** + * Invokes include organizer and returns the new contents of the translation unit. + */ + private String organizeIncludes(ITranslationUnit tu) throws Exception { + IHeaderChooser headerChooser = new FirstHeaderChooser(); + IncludeOrganizer organizer = new IncludeOrganizer(tu, index, LINE_DELIMITER, headerChooser); + List edits = organizer.organizeIncludes(ast); + IDocument document = new Document(new String(tu.getContents())); + if (!edits.isEmpty()) { + // Apply text edits. + MultiTextEdit edit = new MultiTextEdit(); + edit.addChildren(edits.toArray(new TextEdit[edits.size()])); + edit.apply(document); + } + return document.get(); + } + + //h1.h + //typedef int my_type; + + //A.h + //class A { + // my_type m1(); + //}; + + //A.cpp + //// Comment line 1 + //// Comment line 2 + // + //// Comment for m1 + //my_type A::m1() { + // return 0; + //} + //==================== + //// Comment line 1 + //// Comment line 2 + // + //#include "A.h" + // + //#include "h1.h" + // + //// Comment for m1 + //my_type A::m1() { + // return 0; + //} + public void testNoExistingIncludes() throws Exception { + assertExpectedResults(); + } + + //B.h + //class B {}; + + //C.h + //class C {}; + + //A.h + //#if !defined(INCLUDE_GUARD) + //#define INCLUDE_GUARD + //// Comment line 1 + //// Comment line 2 + // + //// Comment for A + //class A { + // B f; + // C m(); + //}; + //#endif // INCLUDE_GUARD + //==================== + //#if !defined(INCLUDE_GUARD) + //#define INCLUDE_GUARD + //// Comment line 1 + //// Comment line 2 + // + //#include "B.h" + // + //class C; + // + //// Comment for A + //class A { + // B f; + // C m(); + //}; + //#endif // INCLUDE_GUARD + public void testIncludeGuards() throws Exception { + assertExpectedResults(); + } + + //B.h + //template class B {}; + + //C.h + //class C {}; + + //A.h + //#pragma once + //namespace ns { + //// Comment line 1 + //// Comment line 2 + // + //// Comment for A + //class A : public B {}; + //} // namespace ns + //==================== + //#pragma once + // + //#include "B.h" + //#include "C.h" + // + //namespace ns { + //// Comment line 1 + //// Comment line 2 + // + //// Comment for A + //class A : public B {}; + //} // namespace ns + public void testPragmaOnce() throws Exception { + assertExpectedResults(); + } + + //h1.h + //typedef int Type1; + + //h2.h + //class Type2 {}; + + //h3.h + //enum Type3 { ONE, TWO }; + + //h4.h + //class Unrelated {}; + + //A.h + //#include "h1.h" + //class Type2; + //enum class Type3; + //extern Type1 f1(); + //extern Type2 f2(); + //extern Type3 f3(); + + //A.cpp + //// Comment + // + //#include "h2.h" /* Required */ // another comment + //#include "h1.h" // Unused + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + //==================== + //// Comment + // + //#include "A.h" + // + ////#include "h1.h" // Unused + //#include "h2.h" /* Required */ // another comment + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + // + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + public void testExistingIncludes() throws Exception { + assertExpectedResults(); + } + + //h1.h + //typedef int Type1; + + //h2.h + //class Type2 {}; + + //h3.h + //enum class Type3 { ONE, TWO }; + + //h4.h + //class Unrelated {}; + + //A.h + //#include "h1.h" + //class Type2; + //enum class Type3; + //extern Type1 f1(); + //extern Type2 f2(); + //extern Type3 f3(); + + //A.cpp + //// Comment + // + //#include "h2.h" /* Required */ // another comment + //#include "h1.h" // Unused + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + //==================== + //// Comment + // + //#include "h2.h" /* Required */ // another comment + ////#include "h1.h" // Unused + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + //#include "A.h" + // + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + public void testExistingIncludesNoReordering() throws Exception { + getPreferenceStore().setValue(PreferenceConstants.INCLUDES_ALLOW_REORDERING, false); + assertExpectedResults(); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/DefinitionFinderTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/DefinitionFinderTest.java index 7d9ff572f11..4d08478705d 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/DefinitionFinderTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/DefinitionFinderTest.java @@ -88,7 +88,7 @@ public class DefinitionFinderTest extends RefactoringTestBase { for (IASTDeclaration declaration : ast.getDeclarations()) { if (declaration instanceof IASTSimpleDeclaration) { IASTName name = ((IASTSimpleDeclaration) declaration).getDeclarators()[0].getName(); - assertNotNull(DefinitionFinder.getDefinition(name, refactoringContext, NULL_PROGRESS_MONITOR)); + assertNotNull(DefinitionFinder.getDefinition(name, refactoringContext, npm())); } } } finally { diff --git a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF index eb9c2d464a2..f06ce79399a 100644 --- a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF @@ -42,6 +42,7 @@ Export-Package: org.eclipse.cdt.internal.corext;x-internal:=true, org.eclipse.cdt.internal.ui.refactoring.gettersandsetters;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.hidemethod;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.implementmethod;x-friends:="org.eclipse.cdt.ui.tests", + org.eclipse.cdt.internal.ui.refactoring.includes;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.rename;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.togglefunction;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.utils;x-friends:="org.eclipse.cdt.ui.tests", diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties index 18028baf180..5cdafd68387 100644 --- a/core/org.eclipse.cdt.ui/plugin.properties +++ b/core/org.eclipse.cdt.ui/plugin.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2003, 2012 IBM Corporation, QNX Software Systems, and others. +# Copyright (c) 2003, 2013 IBM Corporation, QNX Software Systems, 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 @@ -196,6 +196,8 @@ CPluginFileTypesPreferencePage.name=File Types CodeStylePreferencePage.name=Code Style codeTemplatePreferencePage.name=Code Templates codeFormatterPreferencePage.name=Formatter +includeStylePreferencePage.name=Include Style +organizeIncludesPreferencePage.name=Organize Includes nameStylePreferencePage.name=Name Style CodeAssistPreferencePage.name=Content Assist CodeAssistAdvancedPreferencePage.name=Advanced @@ -274,6 +276,10 @@ ActionDefinition.openType.description= Open an element in an Editor ActionDefinition.addInclude.name= Add Include ActionDefinition.addInclude.description= Create include statement on selection +#Organize Includes +ActionDefinition.addInclude.name= Organize Includes +ActionDefinition.addInclude.description= Evaluates all required includes and replaces the current includes + #Sort Lines ActionDefinition.sortLines.name= Sort Lines ActionDefinition.sortLines.description= Sort selected lines alphabetically @@ -566,7 +572,9 @@ preferenceKeywords.common=c cpp cplusplus cdt preferenceKeywords.codeformatter=profile codestyle project specific comment indentation brace white space blank line new control statement wrapping tab parenthesis bracket preferenceKeywords.codestyle=class member visibility order ordering preferenceKeywords.codetemplates=comment code constructor method file type content -preferenceKeywords.namestyle=name file getter setter field variable +preferenceKeywords.namestyle=name style file getter setter field variable +preferenceKeywords.includestyle=include includes style partner system header file system +preferenceKeywords.organizeincludes=include includes organize preferenceKeywords.todo=case sensitive task tag todo xxx fix fixme project comments preferenceKeywords.indexer=index skip references type macro search build configuration cache memory performance @@ -589,6 +597,7 @@ renameParticipant.name = Source Folder Rename FormatAction.label= &Format IndentAction.label= Correct &Indentation +OrganizeIncludesAction.label= Or&ganize Includes AddIncludeAction.label= A&dd Include SortLinesAction.label= Sor&t Lines CommentAction.label= Co&mment diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index f5655de04b1..901b156a59f 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -1232,6 +1232,22 @@ + + + + + + + + + + @@ -1862,6 +1884,13 @@ menubarPath="org.eclipse.jdt.ui.source.menu/importGroup" id="org.eclipse.cdt.ui.actions.SortLines"> + + + + + + @@ -4606,6 +4647,9 @@ + + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java new file mode 100644 index 00000000000..5c4545bca92 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui; + +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.IJobManager; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.IProgressService; + +import org.eclipse.cdt.ui.CUIPlugin; + +/** + * Synchronously executes a {@link Job} while allowing user to cancel it if it takes too long. + */ +public final class BusyCursorJobRunner { + /** + * Adapts a {@link Job} to be an {@link IRunnableWithProgress}. + */ + private static class JobRunnableWithProgressAdapter implements IRunnableWithProgress { + private final Job job; + + /** + * Creates the {@link IRunnableWithProgress} from the {@link Job}. + */ + public JobRunnableWithProgressAdapter(Job job) { + this.job = job; + } + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + IStatus result; + try { + monitor.beginTask(job.getName(), IProgressMonitor.UNKNOWN); + result = executeAndWait(job, monitor); + } catch (RuntimeException e) { + throw new InvocationTargetException(e); + } + + switch (result.getSeverity()) { + case IStatus.CANCEL: + throw new InterruptedException(); + + case IStatus.ERROR: + if (result.getException() instanceof OperationCanceledException) { + throw new InterruptedException(); + } + throw new InvocationTargetException(new CoreException(result)); + } + } + } + + /** + * Runs the given job and waits for it to finish. If executing in the UI thread, sets the cursor + * to busy while the job is being executed. + * + * @param job the job to execute + * @return the status reflecting the result of the job execution + */ + public static IStatus execute(Job job) { + boolean inUiThread = Thread.currentThread() == Display.getDefault().getThread(); + if (inUiThread) { + return busyCursorWhile(job); + } + return executeAndWait(job, new NullProgressMonitor()); + } + + private static IStatus busyCursorWhile(Job job) { + try { + IProgressService progressService = PlatformUI.getWorkbench().getProgressService(); + progressService.busyCursorWhile(new JobRunnableWithProgressAdapter(job)); + } catch (InterruptedException e) { + return Status.CANCEL_STATUS; // Operation was cancelled. + } catch (InvocationTargetException e) { + Throwable targetException = e.getTargetException(); + if (targetException instanceof CoreException) { + return ((CoreException) targetException).getStatus(); + } + return new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, e.getMessage(), e); + } + return Status.OK_STATUS; + } + + private static IStatus executeAndWait(final Job job, IProgressMonitor monitor) { + final IStatus[] statusHolder = new IStatus[1]; + + IJobManager jobManager = Job.getJobManager(); + JobChangeAdapter listener = new JobChangeAdapter() { + @Override + public void done(IJobChangeEvent event) { + super.done(event); + if (event.getJob() == job) { + synchronized (statusHolder) { + statusHolder[0] = event.getResult(); + statusHolder.notifyAll(); + } + } + } + }; + jobManager.addJobChangeListener(listener); + job.schedule(); + + try { + synchronized (statusHolder) { + while (statusHolder[0] == null) { + try { + statusHolder.wait(100); + if (monitor.isCanceled()) { + job.cancel(); + } + } catch (InterruptedException e) { + job.cancel(); + return Status.CANCEL_STATUS; + } + } + + return statusHolder[0]; + } + } finally { + monitor.done(); + jobManager.removeJobChangeListener(listener); + } + } + + private BusyCursorJobRunner() {} +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java index fd83696f119..a142be73da1 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2010 IBM Corporation and others. + * Copyright (c) 2006, 2013 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 @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * QNX Software System * Anton Leherbauer (Wind River Systems) + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.ui; @@ -19,7 +20,6 @@ import org.eclipse.cdt.ui.CUIPlugin; *

* This interface contains constants only; it is not intended to be implemented or extended. *

- * */ public interface ICHelpContextIds { public static final String PREFIX = CUIPlugin.PLUGIN_ID + "."; //$NON-NLS-1$ @@ -33,9 +33,10 @@ public interface ICHelpContextIds { public static final String OPEN_PROJECT_WIZARD_ACTION = PREFIX + "open_project_wizard_action"; //$NON-NLS-1$ public static final String CONVERT_TO_CCPP_WIZARD_PAGE = PREFIX + "cdt_t_conv_proj_context"; //$NON-NLS-1$ public static final String NEW_C_FILE_WIZARD_PAGE = PREFIX + "cdt_creating_cpp_file_context"; //$NON-NLS-1$ - // Actions + // Actions public static final String ADD_INCLUDE_ON_SELECTION_ACTION = PREFIX + "add_includes_on_selection_action_context"; //$NON-NLS-1$; + public static final String ORGANIZE_INCLUDES_ACTION = PREFIX + "organize_includes_action"; //$NON-NLS-1$; public static final String FILTER_PUBLIC_ACTION= PREFIX + "filter_public_action"; //$NON-NLS-1$ public static final String FILTER_FIELDS_ACTION= PREFIX + "filter_fields_action"; //$NON-NLS-1$ public static final String FILTER_STATIC_ACTION= PREFIX + "filter_static_action"; //$NON-NLS-1$ @@ -87,6 +88,8 @@ public interface ICHelpContextIds { public static final String SPELLING_CONFIGURATION_BLOCK= PREFIX + "spelling_configuration_block_context"; //$NON-NLS-1$ public static final String CODE_STYLE_PREFERENCE_PAGE = PREFIX + "code_style_preference_context"; //$NON-NLS-1$ public static final String CODE_TEMPLATES_PREFERENCE_PAGE = PREFIX + "code_templates_preference_context"; //$NON-NLS-1$ + public static final String INCLUDE_STYLE_PREFERENCE_PAGE = PREFIX + "include_style_preference_context"; //$NON-NLS-1$ + public static final String ORGANIZE_INCLUDES_PREFERENCE_PAGE = PREFIX + "organize_includes_preference_context"; //$NON-NLS-1$ public static final String NAME_STYLE_PREFERENCE_PAGE = PREFIX + "name_style_preference_context"; //$NON-NLS-1$ // Console view diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java index 1338d9d257a..4894e4aa2c8 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java @@ -33,14 +33,14 @@ import org.eclipse.ui.texteditor.RetargetTextEditorAction; import org.eclipse.cdt.ui.actions.CdtActionConstants; import org.eclipse.cdt.internal.ui.IContextMenuConstants; +import org.eclipse.cdt.internal.ui.actions.FindWordAction; +import org.eclipse.cdt.internal.ui.actions.GoToNextPreviousMemberAction; +import org.eclipse.cdt.internal.ui.actions.GotoNextBookmarkAction; +import org.eclipse.cdt.internal.ui.actions.StructureSelectEnclosingAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectHistoryAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectNextAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectPreviousAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectionAction; -import org.eclipse.cdt.internal.ui.actions.StructureSelectEnclosingAction; -import org.eclipse.cdt.internal.ui.actions.FindWordAction; -import org.eclipse.cdt.internal.ui.actions.GoToNextPreviousMemberAction; -import org.eclipse.cdt.internal.ui.actions.GotoNextBookmarkAction; public class CEditorActionContributor extends TextEditorActionContributor { @@ -229,6 +229,7 @@ public class CEditorActionContributor extends TextEditorActionContributor { bars.setGlobalActionHandler(CdtActionConstants.REMOVE_BLOCK_COMMENT, getAction(textEditor, "RemoveBlockComment")); //$NON-NLS-1$ bars.setGlobalActionHandler(CdtActionConstants.INDENT, getAction(textEditor, "Indent")); //$NON-NLS-1$ bars.setGlobalActionHandler(CdtActionConstants.ADD_INCLUDE, getAction(textEditor, "AddIncludeOnSelection")); //$NON-NLS-1$ + bars.setGlobalActionHandler(CdtActionConstants.ORGANIZE_INCLUDES, getAction(textEditor, "OrganizeIncludes")); //$NON-NLS-1$ bars.setGlobalActionHandler(CdtActionConstants.SORT_LINES, getAction(textEditor, "SortLines")); //$NON-NLS-1$ IAction action= getAction(textEditor, ITextEditorActionConstants.REFRESH); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java index 9c27d36db58..5d0e53a8d03 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java @@ -36,6 +36,15 @@ public final class CEditorMessages extends NLS { public static String AddIncludeOnSelection_insertion_failed; public static String AddIncludeOnSelection_help_provider_error; public static String AddIncludesOperation_description; + public static String OrganizeIncludes_label; + public static String OrganizeIncludes_description; + public static String OrganizeIncludes_action; + public static String OrganizeIncludes_error_title; + public static String OrganizeIncludes_insertion_failed; + public static String OrganizeIncludes_help_provider_error; + public static String OrganizeIncludes_failed; + public static String OrganizeIncludes_choose_header; + public static String OrganizeIncludesOperation_description; public static String ShowInCView_description; public static String ShowInCView_label; public static String ShowInCView_tooltip; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties index c189c20f1a1..62aa9c4416e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties @@ -1,5 +1,5 @@ ######################################### -# Copyright (c) 2005, 2011 IBM Corporation and others. +# Copyright (c) 2005, 2012 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 @@ -12,6 +12,7 @@ # Markus Schorn (Wind River Systems) # Sergey Prigogin (Google) # Tomasz Wesolowski +# Mathias Kunter ######################################### AddIncludeOnSelection_label=Add Include @@ -21,6 +22,16 @@ AddIncludeOnSelection_insertion_failed=Adding include statements failed AddIncludeOnSelection_help_provider_error=Help provider error AddIncludesOperation_description=Adding include statement +OrganizeIncludes_label=Organize Includes +OrganizeIncludes_description=Organize includes for current file +OrganizeIncludes_action=Organizing includes +OrganizeIncludes_error_title=Error Organizing Includes +OrganizeIncludes_insertion_failed=Adding include statements failed +OrganizeIncludes_help_provider_error=Help provider error +OrganizeIncludes_failed=Organize Includes operation failed +OrganizeIncludes_choose_header=Choose a header file to include for symbol ''{0}'' +OrganizeIncludesOperation_description=Organizing include statements + ShowInCView_description=Show the current resource in the C/C++ Projects view ShowInCView_label=Show in C/C++ Projects ShowInCView_tooltip=Show current resource in C/C++ Projects view diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties index d020935426b..bd462477db4 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties @@ -18,6 +18,10 @@ AddIncludeOnSelection.label=A&dd Include AddIncludeOnSelection.description=Add include statement for selected name AddIncludeOnSelection.tooltip=Adds an include statement for selected name +OrganizeIncludes.label=Or&ganize Includes +OrganizeIncludes.tooltip=Evaluate All Required Includes and Replace the Current Ones +OrganizeIncludes.description=Evaluates all required includes and replaces the current ones + SortLines.label=Sort Lines SortLines.tooltip=Sort Selected Lines Alphabetically SortLines.description=Sorts selected lines alphabetically diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java index 33fd516ea1f..9808ef10801 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java @@ -72,6 +72,12 @@ public interface ICEditorActionDefinitionIds extends ITextEditorActionDefinition */ public static final String ADD_INCLUDE= "org.eclipse.cdt.ui.edit.text.c.add.include"; //$NON-NLS-1$ + /** + * Action definition ID of the source -> organize includes action + * (value "org.eclipse.cdt.ui.edit.text.c.organize.includes"). + */ + public static final String ORGANIZE_INCLUDES= "org.eclipse.cdt.ui.edit.text.c.organize.includes"; //$NON-NLS-1$ + /** * Action definition ID of the open declaration action * (value "org.eclipse.cdt.ui.edit.opendecl"). diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java new file mode 100644 index 00000000000..4adcddebc56 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.editor; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.ElementListSelectionDialog; + +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; + +/** + * Dialog-based header chooser. + */ +public class InteractiveHeaderChooser implements IHeaderChooser { + private final Shell shell; + Map, IPath> userChoiceCache; + + public InteractiveHeaderChooser(Shell shell) { + this.shell = shell; + userChoiceCache = new HashMap, IPath>(); + } + + @Override + public IPath chooseHeader(final String bindingName, Collection headers) { + if (headers.isEmpty()) + return null; + if (headers.size() == 1) + return headers.iterator().next(); + + Set cacheKey = new HashSet(headers); + // Check the decision cache. If the cache doesn't help, ask the user. + // Query the cache. + if (userChoiceCache.containsKey(cacheKey)) { + return userChoiceCache.get(cacheKey); + } + + // Ask the user. + final IPath[] elemArray = headers.toArray(new IPath[headers.size()]); + final IPath[] selectedElement = new IPath[1]; + runInUIThread(new Runnable() { + @Override + public void run() { + if (!shell.isDisposed()) { + ElementListSelectionDialog dialog = + new ElementListSelectionDialog(shell, new LabelProvider()); + dialog.setElements(elemArray); + dialog.setTitle(CEditorMessages.OrganizeIncludes_label); + dialog.setMessage(NLS.bind(CEditorMessages.OrganizeIncludes_choose_header, bindingName)); + if (dialog.open() == Window.OK) { + selectedElement[0] = (IPath) dialog.getFirstResult(); + } + } + } + }); + + IPath selectedHeader = selectedElement[0]; + if (selectedHeader != null) + userChoiceCache.put(headers, selectedHeader); // Remember user's choice. + return selectedHeader; + } + + private void runInUIThread(Runnable runnable) { + if (Display.getCurrent() != null) { + runnable.run(); + } else { + Display.getDefault().syncExec(runnable); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java new file mode 100644 index 00000000000..72dcc7d6c5e --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2012 Mathias Kunter 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mathias Kunter - initial API and implementation + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.editor; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.text.edits.MalformedTreeException; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.TextEdit; +import org.eclipse.text.undo.DocumentUndoManagerRegistry; +import org.eclipse.text.undo.IDocumentUndoManager; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.ui.texteditor.TextEditorAction; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexManager; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.text.SharedASTJob; + +import org.eclipse.cdt.internal.ui.BusyCursorJobRunner; +import org.eclipse.cdt.internal.ui.ICHelpContextIds; +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer; + +/** + * Organizes the include directives and forward declarations of a source or header file. + */ +public class OrganizeIncludesAction extends TextEditorAction { + /** + * Constructor + * @param editor The editor on which this organize includes action should operate. + */ + public OrganizeIncludesAction(ITextEditor editor) { + super(CEditorMessages.getBundleForConstructedKeys(), "OrganizeIncludes.", editor); //$NON-NLS-1$ + CUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(this, ICHelpContextIds.ORGANIZE_INCLUDES_ACTION); + } + + @Override + public void run() { + final ITextEditor editor = getTextEditor(); + final ITranslationUnit tu = getTranslationUnit(editor); + if (tu == null) { + return; + } + if (!validateEditorInputState()) { + return; + } + + final IHeaderChooser headerChooser = new InteractiveHeaderChooser(editor.getSite().getShell()); + final String lineDelimiter = getLineDelimiter(editor); + final List edits = new ArrayList(); + SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) { + @Override + public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException { + IIndex index= CCorePlugin.getIndexManager().getIndex(tu.getCProject(), + IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT); + try { + index.acquireReadLock(); + IncludeOrganizer organizer = new IncludeOrganizer(tu, index, lineDelimiter, headerChooser); + edits.addAll(organizer.organizeIncludes(ast)); + return Status.OK_STATUS; + } catch (InterruptedException e) { + return Status.CANCEL_STATUS; + } finally { + index.releaseReadLock(); + } + } + }; + IStatus status = BusyCursorJobRunner.execute(job); + if (status.isOK()) { + if (!edits.isEmpty()) { + // Apply text edits. + MultiTextEdit edit = new MultiTextEdit(); + edit.addChildren(edits.toArray(new TextEdit[edits.size()])); + IEditorInput editorInput = editor.getEditorInput(); + IDocument document = editor.getDocumentProvider().getDocument(editorInput); + IDocumentUndoManager manager= DocumentUndoManagerRegistry.getDocumentUndoManager(document); + manager.beginCompoundChange(); + try { + edit.apply(document); + } catch (MalformedTreeException e) { + CUIPlugin.log(e); + } catch (BadLocationException e) { + CUIPlugin.log(e); + } + manager.endCompoundChange(); + } + } else if (status.matches(IStatus.ERROR)) { + ErrorDialog.openError(editor.getEditorSite().getShell(), + CEditorMessages.OrganizeIncludes_error_title, + CEditorMessages.OrganizeIncludes_insertion_failed, status); + } + } + + private static String getLineDelimiter(ITextEditor editor) { + try { + IEditorInput editorInput = editor.getEditorInput(); + IDocument document = editor.getDocumentProvider().getDocument(editorInput); + String delim= document.getLineDelimiter(0); + if (delim != null) { + return delim; + } + } catch (BadLocationException e) { + } + return System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$ + } + + @Override + public void update() { + ITextEditor editor = getTextEditor(); + setEnabled(editor != null && getTranslationUnit(editor) != null); + } + + /** + * Returns the translation unit of the given editor. + * @param editor The editor. + * @return The translation unit. + */ + private static ITranslationUnit getTranslationUnit(ITextEditor editor) { + if (editor == null) { + return null; + } + return CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput()); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeCategoriesBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeCategoriesBlock.java new file mode 100644 index 00000000000..70aa8b24166 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeCategoriesBlock.java @@ -0,0 +1,293 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.preferences; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.layout.PixelConverter; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; + +import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; +import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.IncludeKind; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.ITreeListAdapter; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.TreeListDialogField; + +/** + * The preference block for configuring styles of different categories of include statements. + */ +public class IncludeCategoriesBlock extends OptionsConfigurationBlock { + private final List styles; + private final Map categories = new HashMap(); + private TreeListDialogField categoryTree; + private PixelConverter pixelConverter; + private StackLayout editorAreaStack; + private Category selectedCategory; + + public IncludeCategoriesBlock(IStatusChangeListener context, IProject project, + IWorkbenchPreferenceContainer container, List styles) { + super(context, project, new Key[0], container); + this.styles = styles; + createCategories(); + } + + private void createCategories() { + createCategory(IncludeKind.RELATED); + createCategory(IncludeKind.PARTNER); + createCategory(IncludeKind.IN_SAME_FOLDER); + createCategory(IncludeKind.IN_SUBFOLDER); + createCategory(IncludeKind.SYSTEM); + createCategory(IncludeKind.SYSTEM_WITH_EXTENSION); + createCategory(IncludeKind.SYSTEM_WITHOUT_EXTENSION); + createCategory(IncludeKind.OTHER); + createCategory(IncludeKind.IN_SAME_PROJECT); + createCategory(IncludeKind.IN_OTHER_PROJECT); + createCategory(IncludeKind.EXTERNAL); + } + + private Category createCategory(IncludeKind includeKind) { + Category parentCategory = categories.get(includeKind.parent); + Category category = new Category(includeKind, parentCategory); + categories.put(category.getIncludeKind(), category); + return category; + } + + public void postSetSelection(Object element) { + categoryTree.postSetSelection(new StructuredSelection(element)); + } + + @Override + protected Control createContents(Composite parent) { + pixelConverter = new PixelConverter(parent); + + setShell(parent.getShell()); + + Composite composite = new Composite(parent, SWT.NONE); + composite.setFont(parent.getFont()); + + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + composite.setLayout(layout); + + IncludeStyleAdapter adapter = new IncludeStyleAdapter(); + categoryTree = new TreeListDialogField(adapter, null, new IncludeStyleLabelProvider()); + categoryTree.setDialogFieldListener(adapter); + categoryTree.setLabelText(PreferencesMessages.IncludeCategoriesBlock_header_categories); + categoryTree.setViewerComparator(adapter); + + for (Category category : categories.values()) { + if (category.parent == null) + categoryTree.addElement(category); + } + + Label label = categoryTree.getLabelControl(composite); + label.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false, false)); + + Control tree = categoryTree.getTreeControl(composite); + GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); + gd.widthHint = pixelConverter.convertWidthInCharsToPixels(50); + gd.heightHint = pixelConverter.convertHeightInCharsToPixels(2); + tree.setLayoutData(gd); + + createCategoryEditors(composite); + + categoryTree.setTreeExpansionLevel(2); + categoryTree.selectFirstElement(); + + updateControls(); + return composite; + } + + private void createCategoryEditors(Composite parent) { + Composite editorArea = new Composite(parent, SWT.NONE); + editorArea.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false)); + editorArea.setFont(parent.getFont()); + editorAreaStack = new StackLayout(); + editorArea.setLayout(editorAreaStack); + Map stylesByKind = new HashMap(); + for (IncludeGroupStyle style : styles) { + if (style.getIncludeKind() != IncludeKind.MATCHING_PATTERN) + stylesByKind.put(style.getIncludeKind(), style); + } + + for (Category category : categories.values()) { + IncludeGroupStyleBlock block = new IncludeGroupStyleBlock(fContext, fProject, fContainer, + category.getDescription()); + IncludeGroupStyle style = stylesByKind.get(category.getIncludeKind()); + block.setStyle(style); + Control composite = block.createContents(editorArea); + category.setEditor(block, composite); + } + } + + @Override + protected void updateControls() { + super.updateControls(); + // Refresh + categoryTree.refresh(); + updateConfigurationBlock(categoryTree.getSelectedElements()); + for (Category category : categories.values()) { + category.getEditor().updateControls(); + } + } + + private void updateConfigurationBlock(List selection) { + if (selection.size() == 0) + return; + selectedCategory = (Category) selection.get(0); + editorAreaStack.topControl = selectedCategory.getEditorArea(); + editorAreaStack.topControl.getParent().layout(); + } + + @Override + protected void validateSettings(Key changedKey, String oldValue, String newValue) { + fContext.statusChanged(new StatusInfo()); + } + + /** + * Represents a category of settings. + */ + private final static class Category { + public final Category parent; + public final int index; // Index in the siblings list + private final List children; + private final IncludeKind includeKind; + private Control editorArea; + private IncludeGroupStyleBlock editor; + + Category(IncludeKind includeKind, Category parent) { + this.includeKind = includeKind; + this.parent = parent; + children = new ArrayList(); + index = parent != null ? parent.addChild(this) : 0; + } + + private int addChild(Category category) { + children.add(category); + return children.size() - 1; + } + + Category[] getChildren() { + return children.toArray(new Category[children.size()]); + } + + boolean hasChildren() { + return !children.isEmpty(); + } + + @Override + public String toString() { + return includeKind.name; + } + + IncludeKind getIncludeKind() { + return includeKind; + } + + IncludeGroupStyleBlock getEditor() { + return editor; + } + + Control getEditorArea() { + return editorArea; + } + + void setEditor(IncludeGroupStyleBlock editor, Control editorArea) { + this.editor = editor; + this.editorArea = editorArea; + } + + String getName() { + return includeKind.name; + } + + String getDescription() { + return includeKind.description; + } + } + + private class IncludeStyleAdapter extends ViewerComparator + implements ITreeListAdapter, IDialogFieldListener { + @Override + public void selectionChanged(TreeListDialogField field) { + updateConfigurationBlock(field.getSelectedElements()); + } + + @Override + public void customButtonPressed(TreeListDialogField field, int index) { + } + + @Override + public void doubleClicked(TreeListDialogField field) { + } + + @Override + public Category[] getChildren(TreeListDialogField field, Object element) { + return ((Category) element).getChildren(); + } + + @Override + public Category getParent(TreeListDialogField field, Object element) { + return ((Category) element).parent; + } + + @Override + public boolean hasChildren(TreeListDialogField field, Object element) { + return ((Category) element).hasChildren(); + } + + @Override + public void dialogFieldChanged(DialogField field) { + } + + @Override + public void keyPressed(TreeListDialogField field, KeyEvent event) { + } + + @Override + public int category(Object element) { + return ((Category) element).index; + } + } + + private static class IncludeStyleLabelProvider extends LabelProvider { + @Override + public Image getImage(Object element) { + return null; + } + + @Override + public String getText(Object element) { + return ((Category) element).getName(); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeGroupStyleBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeGroupStyleBlock.java new file mode 100644 index 00000000000..487e722436a --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeGroupStyleBlock.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.preferences; + +import java.util.ArrayList; + +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.layout.PixelConverter; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Widget; +import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; + +import org.eclipse.cdt.utils.ui.controls.ControlFactory; + +import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; +import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle; + +/** + * The preference block for configuring styles of different categories of include statements. + */ +public class IncludeGroupStyleBlock extends OptionsConfigurationBlock { + private final String description; + private IncludeGroupStyle style; + private final ArrayList