From 0a75ee2b57c7b5fff0fec3a1dad194dc7aa6e413 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Tue, 16 Oct 2012 16:01:21 -0700 Subject: [PATCH 01/32] Bug 45203. Initial commit for Organize Includes feature. --- .../scanner/IncludeSearchPathElement.java | 59 +- .../includes/BindingClassifierTest.java | 112 ++ core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF | 1 + .../internal/ui/editor/CEditorMessages.java | 7 + .../ui/editor/CEditorMessages.properties | 11 +- .../ConstructedCEditorMessages.properties | 4 + .../ui/editor/OrganizeIncludesAction.java | 98 ++ .../includes/BindingClassifier.java | 1040 +++++++++++++++++ .../includes/HeaderSubstitutor.java | 713 +++++++++++ .../ui/refactoring/includes/IncludeInfo.java | 83 ++ .../ui/refactoring/includes/IncludeMap.java | 116 ++ .../includes/IncludeOrganizer.java | 704 +++++++++++ .../includes/IncludePreferences.java | 319 +++++ .../ui/refactoring/includes/IncludeUtil.java | 68 ++ .../includes/InclusionContext.java | 186 +++ .../includes/InclusionRequest.java | 78 ++ .../ui/refactoring/includes/Messages.java | 25 + .../refactoring/includes/Messages.properties | 11 + .../eclipse/cdt/ui/PreferenceConstants.java | 6 +- .../cdt/ui/actions/GenerateActionGroup.java | 17 +- 20 files changed, 3644 insertions(+), 14 deletions(-) create mode 100644 core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties 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/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..6a2f0f19728 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2012 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.Arrays; +import java.util.HashSet; +import java.util.Set; + +import junit.framework.TestSuite; + +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, 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 remaining = new HashSet(Arrays.asList(expectedNames)); + for (IBinding binding : bindings) { + String name = binding.getName(); + if (!remaining.remove(name)) { + fail("Binding \"" + name + "\" should not be " + verb); + } + } + if (!remaining.isEmpty()) + fail("Binding \"" + remaining.iterator().next() + "\" is not " + verb); + } + + // 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"); + } +} diff --git a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF index 80a26f88146..1d46a1d536b 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/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..0866f7cc9e9 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,13 @@ 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_error_title; + public static String OrganizeIncludes_insertion_failed; + public static String OrganizeIncludes_help_provider_error; + public static String OrganizeIncludes_failed; + 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..bf34fb659c8 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,14 @@ 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_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 +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/OrganizeIncludesAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java new file mode 100644 index 00000000000..8ca3aae0858 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.editor; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +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.ICHelpContextIds; +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) { + // TODO Fix ID's + super(CEditorMessages.getBundleForConstructedKeys(), "OrganizeIncludes.", editor); //$NON-NLS-1$ + CUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(this, ICHelpContextIds.ADD_INCLUDE_ON_SELECTION_ACTION); + } + + /** + * 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()); + } + + @Override + public void run() { + final ITextEditor editor = getTextEditor(); + final ITranslationUnit tu = getTranslationUnit(editor); + if (tu == null) { + return; + } + try { + if (!validateEditorInputState()) { + return; + } + + SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_label, 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(editor, tu, index); + organizer.organizeIncludes(ast); + return Status.OK_STATUS; + } catch (InterruptedException e) { + return Status.CANCEL_STATUS; + } finally { + index.releaseReadLock(); + } + } + }; + job.schedule(); + job.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + @Override + public void update() { + ITextEditor editor = getTextEditor(); + setEnabled(editor != null && getTranslationUnit(editor) != null); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java new file mode 100644 index 00000000000..81d76f66f4a --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java @@ -0,0 +1,1040 @@ +/******************************************************************************* + * Copyright (c) 2012 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 + * Mathias Kunter + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.includes; + +import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.ALLCVQ; +import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.ARRAY; +import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.PTR; +import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.REF; +import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; +import org.eclipse.cdt.core.dom.ast.IASTCastExpression; +import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression; +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTDoStatement; +import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer; +import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTFieldReference; +import org.eclipse.cdt.core.dom.ast.IASTForStatement; +import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.IASTIdExpression; +import org.eclipse.cdt.core.dom.ast.IASTIfStatement; +import org.eclipse.cdt.core.dom.ast.IASTInitializer; +import org.eclipse.cdt.core.dom.ast.IASTInitializerClause; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTPointerOperator; +import org.eclipse.cdt.core.dom.ast.IASTReturnStatement; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; +import org.eclipse.cdt.core.dom.ast.IASTWhileStatement; +import org.eclipse.cdt.core.dom.ast.IBasicType; +import org.eclipse.cdt.core.dom.ast.IBasicType.Kind; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.ICompositeType; +import org.eclipse.cdt.core.dom.ast.IEnumeration; +import org.eclipse.cdt.core.dom.ast.IEnumerator; +import org.eclipse.cdt.core.dom.ast.IFunction; +import org.eclipse.cdt.core.dom.ast.IParameter; +import org.eclipse.cdt.core.dom.ast.IPointerType; +import org.eclipse.cdt.core.dom.ast.IProblemBinding; +import org.eclipse.cdt.core.dom.ast.IType; +import org.eclipse.cdt.core.dom.ast.ITypedef; +import org.eclipse.cdt.core.dom.ast.IVariable; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeleteExpression; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter; +import org.eclipse.cdt.core.index.IIndexMacro; +import org.eclipse.cdt.core.index.IndexFilter; + +/** + * The inclusion resolver finds the bindings which have to be included from a given AST. + */ +public class BindingClassifier { + private final InclusionContext fContext; + + /** Stores the AST node which this resolver is working on. */ + private final IASTNode fNode; + + /** Stores the bindings which require a full definition. */ + private final Set fBindingsToDefine; + + /** Stores the bindings which only require a simple forward declaration. */ + private final Set fBindingsToDeclare; + private final IncludePreferences fPreferences; + + /** + * Constructs a new inclusion resolver. + * + * @param context the context for binding classification + * @param node the AST node which should be processed by the resolver + */ + public BindingClassifier(InclusionContext context, IASTNode node) { + fContext = context; + fPreferences = context.getPreferences(); + fNode = node; + fBindingsToDefine = new HashSet(); + fBindingsToDeclare = new HashSet(); + + processNode(fNode); + } + + /** + * Returns the bindings which require a full definition. + */ + public Set getBindingsToDefine() { + return fBindingsToDefine; + } + + /** + * Returns the bindings which only require a simple forward declaration. + */ + public Set getBindingsToDeclare() { + return fBindingsToDeclare; + } + + /** + * Processes the given node and all of its children recursively. + * + * @param node The node to process. + */ + private void processNode(IASTNode node) { + if (processSingleNode(node)) { + // Process all children of this node as well. + IASTNode[] children = node.getChildren(); + for (IASTNode child : children) { + processNode(child); + } + } + } + + /** + * Processes the given node, but none of its children. + * + * @param node The node to process. + * @return Whether the children of the node must be processed. + */ + private boolean processSingleNode(IASTNode node) { + if (node == null) { + return false; + } + + if (node instanceof IASTSimpleDeclaration) { + /* + * The type specifier of a simple declaration of a variable must always be defined + * except within the situations shown by the following examples: + * + * Example 1: + * X* x; // definition of X is not required here - pointer type + * + * Example 2: + * X& x; // definition of X is not required here - reference type + * + * The type specifier of a simple function declaration also doesn't need to be defined: + * + * Example 3: + * X foo(); // definition of X is not required here + * + * The type specifier of static member declarations also doesn't need to be defined: + * + * Example 4: + * class Y { + * static X x; // definition of X is not required here + * }; + */ + IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) node; + IASTDeclSpecifier declSpecifier = simpleDeclaration.getDeclSpecifier(); + IASTDeclarator[] declarators = simpleDeclaration.getDeclarators(); + + if (declSpecifier instanceof IASTNamedTypeSpecifier) { + // We only handle simple declarations here whose declaration specifiers are named + // type specifiers. + boolean staticMember = + simpleDeclaration.getParent() instanceof IASTCompositeTypeSpecifier && + declSpecifier.getStorageClass() == IASTDeclSpecifier.sc_static; + + // Declare the named type specifier if all declarators are either pointers, + // references or functions. + boolean canBeDeclared = true; + if (!staticMember) { + for (IASTDeclarator declarator : declarators) { + if (!(declarator instanceof IASTFunctionDeclarator) && + declarator.getPointerOperators().equals(IASTPointerOperator.EMPTY_ARRAY)) { + canBeDeclared = false; + break; + } + } + } + + if (!canBeDeclared) { + defineBinding(((IASTNamedTypeSpecifier) declSpecifier).getName().resolveBinding()); + } + } + } else if (node instanceof ICPPASTBaseSpecifier) { + /* + * The type of a base specifier must always be defined. + * + * Example: + * class Y : X {}; // definition of X is required here + */ + defineBinding(((ICPPASTBaseSpecifier) node).getName().resolveBinding()); + } else if (node instanceof IASTInitializer) { + /* + * The type of the member of a constructor chain initializer doesn't need to be defined + * if it is a pointer or reference type and if the argument type matches. + * + * Example 1: + * class X { + * X* x1; + * X(X* x2) : + * x1(x2) {} // definition of typeof(x1) is not required here + * }; + * + * Example 2: + * class X { + * X& x1; + * X(X& x2) : + * x1(x2) {} // definition of typeof(x1) is not required here + * }; + * + * The type of a constructor initializer doesn't need to be defined if it matches with + * the type of the declaration. + * + * Example: + * void foo(X& x1) { + * X& x2(x1); // definition of typeof(x1) is not required here + * } + * + * The type of an equals initializer doesn't need to be defined if it matches with + * the type of the declaration. + * + * Example: + * void foo(X& x1) { + * X& x2 = x1; // definition of typeof(x1) is not required here + * } + */ + + IASTInitializer initializer = (IASTInitializer) node; + + // Get the binding of the initialized AST name first. + IASTNode memberNode = node; + IASTName memberName = null; + IBinding memberBinding = null; + + while (memberNode != null) { + if (memberNode instanceof IASTDeclarator) { + memberName = ((IASTDeclarator) memberNode).getName(); + break; + } else if (memberNode instanceof ICPPASTConstructorChainInitializer) { + memberName = ((ICPPASTConstructorChainInitializer) memberNode).getMemberInitializerId(); + break; + } + memberNode = memberNode.getParent(); + } + if (memberName != null) { + memberBinding = memberName.resolveBinding(); + } + + // Get the arguments of the initializer. + IASTInitializerClause[] actualParameters = new IASTInitializerClause[] { }; + if (initializer instanceof ICPPASTConstructorInitializer) { + ICPPASTConstructorInitializer constructorInitializer = (ICPPASTConstructorInitializer) initializer; + actualParameters = constructorInitializer.getArguments(); + } else if (initializer instanceof IASTEqualsInitializer) { + IASTEqualsInitializer equalsInitializer = (IASTEqualsInitializer) initializer; + actualParameters = new IASTInitializerClause[] { equalsInitializer.getInitializerClause() }; + } + + if (memberBinding instanceof IVariable) { + // Variable construction. + IType memberType = ((IVariable) memberBinding).getType(); + if (!(memberType instanceof IPointerType) && !(memberType instanceof ICPPReferenceType)) { + // We're constructing a non-pointer type. We need to define the member type + // either way since we must be able to call its constructor. + defineTypeExceptTypedef(memberType); + + // TODO: Process the arguments. But how to get the corresponding IParameter[] array here? + // processParameters(declaredParameters, arguments); + } else { + // We're constructing a pointer type. No constructor is called. We however have + // to check whether the argument type matches the declared type. + memberType = getNestedType(memberType, REF); + for (IASTInitializerClause actualParameter : actualParameters) { + if (actualParameter instanceof IASTExpression) { + IType parameterType = ((IASTExpression) actualParameter).getExpressionType(); + if (!isSameType(memberType, parameterType)) { + // Types don't match. Define both types. + defineTypeExceptTypedef(memberType); + defineTypeExceptTypedef(parameterType); + } + } + } + } + } else if (memberBinding instanceof ICPPConstructor) { + // Class construction + ICPPConstructor constructor = (ICPPConstructor) memberBinding; + + // We need to define the owning type of the constructor. + defineBinding(constructor.getOwner()); + + // Process the parameters. + processParameters(constructor.getParameters(), actualParameters); + } + + } else if (node instanceof IASTElaboratedTypeSpecifier) { + /* + * The type specifier of an elaborated type neither needs to be defined nor needs to be + * declared. This is because an elaborated type specifier is a self-sufficient + * statement. + * + * Example: + * class X; // neither definition nor declaration of X is required here + */ + return false; + } else if (node instanceof IASTFunctionDefinition) { + /* + * The type specifier of a function definition doesn't need to be defined if it is + * a pointer or reference type. + * + * Example 1: + * X *foo() { } // definition of X is not required here + * + * Example 2: + * X& foo() { } // definition of X is not required here + */ + IBinding binding = ((IASTFunctionDefinition) node).getDeclarator().getName().resolveBinding(); + if (binding instanceof IFunction) { + IFunction function = (IFunction) binding; + + // Define the return type if necessary + IType returnType = function.getType().getReturnType(); + if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) { + defineTypeExceptTypedef(returnType); + } + + // Define parameter types if necessary + IType[] parameterTypes = function.getType().getParameterTypes(); + for (IType type : parameterTypes) { + if (!(type instanceof IPointerType) && !(type instanceof ICPPReferenceType)) { + defineTypeExceptTypedef(type); + } + } + } + } else if (node instanceof IASTIfStatement || + node instanceof IASTForStatement || + node instanceof IASTWhileStatement || + node instanceof IASTDoStatement) { + IASTExpression conditionExpression = null; + + if (node instanceof IASTIfStatement) { + /* + * The type of the condition expression of an if statement doesn't need to be + * defined if it's a pointer type. + * + * Example: + * void foo(X* x) { + * if (x) { } // definition of typeof(x) is not required here + * } + */ + conditionExpression = ((IASTIfStatement) node).getConditionExpression(); + } else if (node instanceof IASTForStatement) { + /* + * The type of the condition expression of a for statement doesn't need to be + * defined if it's a pointer type. + * + * Example: + * void foo(X* x) { + * for (; x; ) { } // definition of typeof(x) is not required here + * } + */ + conditionExpression = ((IASTForStatement) node).getConditionExpression(); + } else if (node instanceof IASTWhileStatement) { + /* + * The type of the condition expression of a while statement doesn't need to be + * defined if it's a pointer type. + * + * Example: + * void foo(X* x) { + * while (x) { } // definition of typeof(x) is not required here + * } + */ + conditionExpression = ((IASTWhileStatement) node).getCondition(); + } else if (node instanceof IASTDoStatement) { + /* + * The type of the condition expression of a do statement doesn't need to be + * defined if it's a pointer type. + * + * Example: + * void foo(X* x) { + * do { } while (x); // definition of typeof(x) is not required here + * } + */ + conditionExpression = ((IASTDoStatement) node).getCondition(); + } + + if (conditionExpression != null) { + IType conditionExpressionType = conditionExpression.getExpressionType(); + if (!(conditionExpressionType instanceof IPointerType)) { + defineTypeExceptTypedef(conditionExpressionType); + } + } + } else if (node instanceof IASTReturnStatement) { + /* + * The type of the return value expression doesn't need to be defined if the actual + * return type matches the declared return type (i.e. if no implicit conversion is + * necessary). + * + * Example: + * X& foo(X& x) { + * return x; // definition of typeof(x) is not required here + * } + */ + IASTReturnStatement returnStatement = (IASTReturnStatement) node; + + IASTExpression returnValue = returnStatement.getReturnValue(); + if (returnValue != null) { + // Get the containing function definition. + IASTNode functionDefinitionNode = returnStatement; + while (functionDefinitionNode != null && !(functionDefinitionNode instanceof IASTFunctionDefinition)) { + functionDefinitionNode = functionDefinitionNode.getParent(); + } + + // Check whether the declared return type matches the actual return type. + if (functionDefinitionNode != null) { + IASTFunctionDefinition functionDefinition = (IASTFunctionDefinition) functionDefinitionNode; + IASTFunctionDeclarator functionDeclarator = functionDefinition.getDeclarator(); + if (functionDeclarator != null) { + IBinding binding = functionDeclarator.getName().resolveBinding(); + if (binding instanceof IFunction) { + IFunction function = (IFunction) binding; + + // Get the declared return type and the actual expression type. + // Don't care about reference types since they can be converted into + // non-reference types and vice versa without requiring a definition. + IType returnType = function.getType().getReturnType(); + returnType = getNestedType(returnType, REF); + IType expressionType = getNestedType(returnValue.getExpressionType(), REF); + + // Compare the two types. + if (!isSameType(returnType, expressionType)) { + // Not the same type. Define both types. + defineTypeExceptTypedef(returnType); + defineTypeExceptTypedef(expressionType); + } + } + } + } + } + } else if (node instanceof IASTIdExpression) { + /* + * The type of an identifier expression doesn't need to be defined if it's a pointer or + * a reference type. + * + * Example: + * void foo(X& x) { + * x; // definition of typeof(x) is not required here + * } + */ + IASTIdExpression idExpression = (IASTIdExpression) node; + + IBinding binding = idExpression.getName().resolveBinding(); + if (binding instanceof IVariable) { + // Get the declared type. + IType expressionType = ((IVariable) binding).getType(); + if (!(expressionType instanceof IPointerType) && !(expressionType instanceof ICPPReferenceType)) { + defineTypeExceptTypedef(expressionType); + } + } + } else if (node instanceof IASTUnaryExpression) { + /* + * The type of the operand of an unary expression doesn't need to be defined if + * the operator is the ampersand operator: + * + * Example: + * void foo(X& x) { + * &x; // ampersand operator + * } + * + * If the operand is a pointer type, the following operators also don't require + * a definition: + * + * Example: + * void foo(X* x) { + * __alignof(x); // alignof operator + * !x; // not operator + * +x; // unary plus operator + * sizeof(x); // sizeof operator + * typeid(x); // typeid operator + * } + */ + IASTUnaryExpression unaryExpression = (IASTUnaryExpression) node; + + boolean expressionDefinitionRequired = true; + switch (unaryExpression.getOperator()) { + case IASTUnaryExpression.op_amper: + case IASTUnaryExpression.op_bracketedPrimary: + // The ampersand operator as well as brackets never require a definition. + expressionDefinitionRequired = false; + break; + case IASTUnaryExpression.op_alignOf: + case IASTUnaryExpression.op_not: + case IASTUnaryExpression.op_plus: + case IASTUnaryExpression.op_sizeof: + case IASTUnaryExpression.op_typeid: + // If the operand is a pointer type, then it doesn't need to be defined. + if (unaryExpression.getOperand().getExpressionType() instanceof IPointerType) { + expressionDefinitionRequired = false; + } + } + + if (expressionDefinitionRequired) { + defineTypeExceptTypedef(unaryExpression.getOperand().getExpressionType()); + } + } else if (node instanceof IASTBinaryExpression) { + /* + * The types of the operands of a binary expression don't need to be defined for + * the following operators if the operands are pointer types: + * + * Example: + * void foo(X* x) { + * x = x; // assignment operator + * x == x; // equals operator + * x != x; // not equals operator + * x >= x; // greater equal operator + * x > x; // greater operator + * x <= x; // less equal operator + * x < x; // less operator + * x && x; // logical and operator + * x || x; // logical or operator + * } + * + * However, note that if both operands are pointers of different types, then only + * the following operators don't require a definition of the types of the operands: + * + * void foo(X* x, Y* y) { + * x && y; // logical and operator + * x || y; // logical or operator + * } + */ + + IASTBinaryExpression binaryExpression = (IASTBinaryExpression) node; + + IType operand1Type = binaryExpression.getOperand1().getExpressionType(); + IType operand2Type = binaryExpression.getOperand2().getExpressionType(); + + boolean expression1DefinitionRequired = true; + boolean expression2DefinitionRequired = true; + + switch (binaryExpression.getOperator()) { + case IASTBinaryExpression.op_logicalAnd: + case IASTBinaryExpression.op_logicalOr: + // Pointer types don't need to be defined for logical operations. + if (operand1Type instanceof IPointerType) { + expression1DefinitionRequired = false; + } + if (operand2Type instanceof IPointerType) { + expression2DefinitionRequired = false; + } + break; + case IASTBinaryExpression.op_assign: + case IASTBinaryExpression.op_equals: + case IASTBinaryExpression.op_notequals: + case IASTBinaryExpression.op_greaterEqual: + case IASTBinaryExpression.op_greaterThan: + case IASTBinaryExpression.op_lessEqual: + case IASTBinaryExpression.op_lessThan: + // If both operands are identical pointer types, then they don't need to be + // defined. + if (operand1Type instanceof IPointerType && operand2Type instanceof IPointerType) { + if (isSameType(operand1Type, operand2Type)) { + expression1DefinitionRequired = false; + expression2DefinitionRequired = false; + } + } else if (operand1Type instanceof IPointerType) { + // Only the first operand is a pointer type. It doesn't have to be defined. + expression1DefinitionRequired = false; + } else if (operand2Type instanceof IPointerType) { + // Only the second operand is a pointer type. It doesn't have to be defined. + expression2DefinitionRequired = false; + } + } + + if (expression1DefinitionRequired) { + defineTypeExceptTypedef(operand1Type); + } + if (expression2DefinitionRequired) { + defineTypeExceptTypedef(operand2Type); + } + } else if (node instanceof IASTConditionalExpression) { + /* + * The type of the condition of a conditional expression doesn't need to be defined + * if it's a pointer type. + * + * Example: + * void foo(X* x) { + * x ? 1 : 0; // definition of typeof(x) is not required here + * } + */ + + IASTConditionalExpression conditionalExpression = (IASTConditionalExpression) node; + IASTExpression logicalConditionExpression = conditionalExpression.getLogicalConditionExpression(); + + if (logicalConditionExpression != null) { + IType logicalConditionExpressionType = logicalConditionExpression.getExpressionType(); + if (!(logicalConditionExpressionType instanceof IPointerType)) { + defineTypeExceptTypedef(logicalConditionExpressionType); + } + } + } else if (node instanceof IASTFunctionCallExpression) { + /* + * The return type and argument types of a function call expression don't need to be + * defined if they're pointer or reference types. The declared and actual types of + * the arguments must further be identical, since implicit type conversions require + * a definition of both the source and the target type. + * + * Example: + * X& foo(X& x); + * void bar(X& x) { + * foo(x); // definition of typeof(foo) and typeof(x) is not required here + * } + * + * Also note that the function call itself doesn't require a definition as long as + * it's not a constructor call: + * + * Example 1: + * void foo() { + * bar(); // definition of bar() is not required here (assuming bar is a function) + * } + * + * Example 2: + * void foo() { + * X(); // definition of X is required here (assuming X is a composite type) + * } + */ + IASTFunctionCallExpression functionCallExpression = (IASTFunctionCallExpression) node; + IASTExpression functionNameExpression = functionCallExpression.getFunctionNameExpression(); + + if (functionNameExpression instanceof IASTIdExpression) { + IBinding binding = ((IASTIdExpression) functionNameExpression).getName().resolveBinding(); + if (binding instanceof IFunction) { + IFunction function = (IFunction) binding; + // Handle return or expression type of the function or constructor call. + IType returnType = function.getType().getReturnType(); + if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) { + // The return type needs a full definition. + defineTypeExceptTypedef(returnType); + } + + // Handle parameters. + processParameters(function.getParameters(), functionCallExpression.getArguments()); + } else if (binding instanceof IType) { + // The binding resolves to a composite type, as it does for constructor calls. + // We have to define the binding. + defineTypeExceptTypedef((IType) binding); + } + } + } else if (node instanceof IASTFieldReference) { + /* + * The type of the expression part of a field reference always requires a definition. + * + * Example: + * void foo(X& x1, X* x2) { + * x1.bar(); // definition of typeof(x1) is required here + * x2->bar(); // definition of typeof(x2) is required here + * } + */ + + defineTypeExceptTypedef(((IASTFieldReference) node).getFieldOwner().getExpressionType()); + } else if (node instanceof ICPPASTNewExpression) { + /* + * The type specifier of a "new" expression always requires a definition. + * + * Example: + * void foo() { + * new X(); // definition of X is required here + * } + */ + defineTypeExceptTypedef(((ICPPASTNewExpression) node).getExpressionType()); + } else if (node instanceof ICPPASTDeleteExpression) { + /* + * The expression type of a "delete" expression always requires a full definition. + * This is necessary because the compiler needs to be able to call the destructor. + * + * Example: + * void foo(X* x) { + * delete x; // definition of typeof(x) is required here + * } + */ + defineTypeExceptTypedef(((ICPPASTDeleteExpression) node).getOperand().getExpressionType()); + } else if (node instanceof IASTCastExpression) { + /* + * Explicit type casts always need the definition of the underlying types. + * + * Example: + * void foo(X* x) { + * (Y*) x; // definition of both Y and typeof(x) is required here + * } + */ + IASTCastExpression castExpression = (IASTCastExpression) node; + IType targetType = castExpression.getExpressionType(); + IType sourceType = castExpression.getOperand().getExpressionType(); + + if (!isSameType(targetType, sourceType)) { + // Source and target types of the cast expression are different. We need to define + // both types, even if they're pointers. + defineTypeExceptTypedef(targetType); + defineTypeExceptTypedef(sourceType); + } else if (!(targetType instanceof IPointerType) && !(targetType instanceof ICPPReferenceType)) { + // Define the target type if it's not a pointer or reference type. + defineTypeExceptTypedef(targetType); + } + } else if (node instanceof ICPPASTCatchHandler) { + /* + * Catch handles always need the definition of the caught type, even if it's a pointer + * or reference type. + * + * Example: + * void foo() { + * try { + * } catch (X& x) { // Definition of X is required here + * } + * } + */ + ICPPASTCatchHandler catchHandler = (ICPPASTCatchHandler) node; + IASTDeclaration declaration = catchHandler.getDeclaration(); + if (declaration instanceof IASTSimpleDeclaration) { + IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration; + IASTDeclSpecifier declSpecifier = simpleDeclaration.getDeclSpecifier(); + if (declSpecifier instanceof IASTNamedTypeSpecifier) { + IASTNamedTypeSpecifier namedTypeSpecifier = (IASTNamedTypeSpecifier) declSpecifier; + defineBinding(namedTypeSpecifier.getName().resolveBinding()); + } + } + } else if (node instanceof IASTName) { + // We've found an AST name. Add it to the bindings which can be declared (we assume + // that all bindings which have to be defined are already explicitly handled by + // the code above). + IASTName name = (IASTName) node; + + if (name instanceof ICPPASTQualifiedName) { + // Any qualifying names must be defined. + IASTName[] names = ((ICPPASTQualifiedName) name).getNames(); + for (int i = 0; i + 1 < names.length; i++) { + defineBinding(names[i].resolveBinding()); + } + } + + IBinding binding = name.resolveBinding(); + IBinding owner = binding.getOwner(); + if (owner instanceof IType) { + defineBinding(owner); // Member access requires definition of the containing type. + } else { + declareBinding(binding); // Declare the binding of this name. + } + } + + // Return true to signal that all children of this node must be processed as well. + return true; + } + + /** + * Defines the required types of the parameters of a function or constructor call expression by + * comparing the declared parameters with the actual arguments. + */ + private void processParameters(IParameter[] declaredParameters, IASTInitializerClause[] arguments) { + for (int i = 0; i < declaredParameters.length; i++) { + IType declaredParameterType = declaredParameters[i].getType(); + IType actualParameterType = null; + boolean canBeDeclared = false; + if (declaredParameterType instanceof IPointerType || declaredParameterType instanceof ICPPReferenceType) { + // The declared parameter type is a pointer or reference type. A declaration is + // sufficient if it matches the actual parameter type. + declaredParameterType = getNestedType(declaredParameterType, REF); + if (i < arguments.length) { + // This parameter is present within the function call expression. + // It's therefore not a default parameter. + IASTInitializerClause actualParameter = arguments[i]; + if (actualParameter instanceof IASTExpression) { + actualParameterType = ((IASTExpression) actualParameter).getExpressionType(); + actualParameterType = getNestedType(actualParameterType, REF); + + if (isSameType(declaredParameterType, actualParameterType)) { + canBeDeclared = true; + } + } + } else { + // This is a default value parameter. The function call itself doesn't need + // a definition of this parameter type. + canBeDeclared = true; + } + } + + if (canBeDeclared) { + // The declared parameter type must be declared. We must explicitly do this here + // because this type doesn't appear within the AST. + declareType(declaredParameterType); + } else { + // Both the type of the declared parameter as well as the type of the actual + // parameter require a full definition. + defineTypeExceptTypedef(declaredParameterType); + defineTypeExceptTypedef(actualParameterType); + } + } + } + + /** + * Returns whether the two given types are identical. This does the same as IType.isSameType() + * with the exception that it considers a pointer and the zero literal identical. + */ + private boolean isSameType(IType type1, IType type2) { + if (type1 == null || type2 == null) { + return false; + } + if (type1.isSameType(type2)) { + return true; + } + + if (type1 instanceof IPointerType || type2 instanceof IPointerType) { + if ((type1 instanceof IBasicType && ((IBasicType) type1).getKind() == Kind.eInt) + || (type2 instanceof IBasicType && ((IBasicType) type2).getKind() == Kind.eInt)) { + return true; + } + } + + return false; + } + + /** + * Resolves the given binding and returns the binding(s) which we actually have to either + * declare or define. As an example, if the given binding is a variable, this function returns + * the binding for the type of the variable. This is because we actually have to declare or + * define the type of a variable, not the variable itself. + * + * @param binding The binding to resolve. + * @return The binding(s) which is/are suitable for either declaration or definition, + * or an empty list if no such binding is available. + */ + private List resolveBinding(IBinding binding) { + if (binding instanceof IVariable) { + // Resolve the type of the variable. + binding = getTypeBinding(((IVariable) binding).getType()); + } else if (binding instanceof IType) { + // Resolve the type. + binding = getTypeBinding((IType) binding); + } else if (binding instanceof ICPPConstructor) { + // If the binding is a constructor, get its owning composite type. + binding = binding.getOwner(); + } else if (binding instanceof ICPPNamespace) { + // Namespaces are neither declared nor defined. + binding = null; + } + + if (binding instanceof ICPPSpecialization) { + // Get the specialized binding - e.g. get the binding for X if the current binding is + // for the template specialization X. Resolution of the specialization + // (i.e. template) arguments is handled separately by the caller of this method. + binding = ((ICPPSpecialization) binding).getSpecializedBinding(); + } + + List bindings = new ArrayList(); + + if (binding instanceof IProblemBinding) { + IProblemBinding problemBinding = (IProblemBinding) binding; + + IBinding[] candidateBindings = problemBinding.getCandidateBindings(); + if (candidateBindings.length > 0) { + // There are candidate bindings available. We simply use them all here since those + // different candidates are very often defined within the same target file anyway, + // so it won't affect the list of generated include directives. This therefore + // allows us to be a little more fault tolerant here. + for (IBinding candidateBinding : candidateBindings) { + bindings.add(candidateBinding); + } + } else { + // No candidate bindings available. Check whether this is a macro. + try { + IIndexMacro[] indexMacros = fContext.getIndex().findMacros(binding.getNameCharArray(), + IndexFilter.ALL, null); + for (IIndexMacro indexMacro : indexMacros) { + bindings.add(indexMacro); + } + } catch (CoreException e) { + } + } + } else if (binding != null) { + bindings.add(binding); + } + + return bindings; + } + + /** + * Resolves the given type to a binding which we actually have to either declare or define. + * As an example if the given type is a pointer type, this function returns the binding for + * the raw (i.e. nested) type of the pointer. This is because we actually have to declare or + * define the raw type of a pointer, not the pointer type itself. + * + * @param type The type to resolve. + * @return A binding which is suitable for either declaration or definition, or {@code null} + * if no such binding is available. + */ + private IBinding getTypeBinding(IType type) { + type = getNestedType(type, ALLCVQ | PTR | ARRAY | REF); + if (type instanceof IBinding) { + return (IBinding) type; + } + return null; + } + + /** + * Adds the given binding to the list of bindings which have to be forward declared. + * + * @param binding The binding to add. + */ + private void declareBinding(IBinding binding) { + List resolvedBindings = resolveBinding(binding); + + for (IBinding resolvedBinding : resolvedBindings) { + if (fBindingsToDeclare.contains(resolvedBinding) || fBindingsToDefine.contains(resolvedBinding)) { + return; + } + + // Check whether the user actually wants to declare this binding. + boolean doDeclareBinding = true; + if (resolvedBinding instanceof ICompositeType) { + doDeclareBinding = fPreferences.forwardDeclareCompositeTypes; + } else if (resolvedBinding instanceof IEnumeration) { + doDeclareBinding = fPreferences.forwardDeclareEnums; + } else if (resolvedBinding instanceof IFunction && !(resolvedBinding instanceof ICPPMethod)) { + doDeclareBinding = fPreferences.forwardDeclareFunctions; + } else if (resolvedBinding instanceof IIndexMacro || resolvedBinding instanceof ITypedef + || resolvedBinding instanceof IEnumerator) { + // Macros, typedefs and enumerators can never be declared. + doDeclareBinding = false; + } + + if (doDeclareBinding) { + fBindingsToDeclare.add(resolvedBinding); + } else { + fBindingsToDefine.add(resolvedBinding); + } + } + } + + /** + * Adds the given type to the list of bindings which have to be declared. + * + * @param type The type to add. + */ + private void declareType(IType type) { + IBinding typeBinding = getTypeBinding(type); + if (typeBinding != null) + declareBinding(typeBinding); + } + + /** + * Adds the given type to the list of bindings which have to be defined. Typedefs are skipped + * since they must be defined in the file that references them by name. If a typedef is + * explicitly referenced in this translation unit, it will be defined independently from this + * method. + * + * @param type The type to add. + */ + private void defineTypeExceptTypedef(IType type) { + IBinding typeBinding = getTypeBinding(type); + if (typeBinding != null && !(typeBinding instanceof ITypedef)) { + defineBinding(typeBinding); + } + } + + /** + * Adds the given binding to the list of bindings which have to be defined. + * + * @param binding The binding to add. + */ + private void defineBinding(IBinding binding) { + if (binding instanceof ICPPTemplateInstance) { + defineTemplateArguments((ICPPTemplateInstance) binding); + } + + List resolvedBindings = resolveBinding(binding); + for (IBinding resolvedBinding : resolvedBindings) { + fBindingsToDeclare.remove(resolvedBinding); + fBindingsToDefine.add(resolvedBinding); + } + } + + /** + * Defines non-pointer template arguments. + */ + protected void defineTemplateArguments(ICPPTemplateInstance instance) { + ICPPTemplateDefinition templateDefinition = instance.getTemplateDefinition(); + ICPPTemplateParameter[] templateParameters = templateDefinition.getTemplateParameters(); + ICPPTemplateArgument[] templateArguments = instance.getTemplateArguments(); + for (int i = 0; i < templateArguments.length; i++) { + ICPPTemplateArgument argument = templateArguments[i]; + ICPPTemplateParameter parameter = templateParameters[i]; + ICPPTemplateArgument parameterDefault = parameter.getDefaultValue(); + if (parameterDefault != null) { + // Skip the template arguments if it is the same as parameter default. + if (argument.isSameValue(parameterDefault)) + continue; + if (argument.isTypeValue() && parameterDefault.isTypeValue()) { + IType argType = argument.getTypeValue(); + IType defType = parameterDefault.getTypeValue(); + if (argType instanceof ICPPTemplateInstance && defType instanceof ICPPTemplateInstance) { + IType argTemplate = (IType) ((ICPPTemplateInstance) argType).getTemplateDefinition(); + IType defTemplate = (IType) ((ICPPTemplateInstance) defType).getTemplateDefinition(); + if (argTemplate.isSameType(defTemplate)) { + defineTemplateArguments((ICPPTemplateInstance) argType); + continue; + } + } + } + } + IType type = argument.getTypeValue(); + if (!(type instanceof IPointerType) && !(type instanceof ICPPReferenceType)) { + IBinding binding = getTypeBinding(type); + if (binding != null) + defineBinding(binding); + } + } + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java new file mode 100644 index 00000000000..abb3e83042a --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java @@ -0,0 +1,713 @@ +/******************************************************************************* + * Copyright (c) 2012 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.refactoring.includes; + +import java.net.URI; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; + +import org.eclipse.cdt.core.index.IIndexFile; +import org.eclipse.cdt.core.index.IIndexInclude; +import org.eclipse.cdt.core.index.IndexLocationFactory; +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.core.resources.ResourceLookup; + +public class HeaderSubstitutor { + IncludeMap[] INCLUDE_MAPS = { + cIncludeMap, cIncludeMapWeak, cppIncludeMap, cppIncludeMapWeak, + google3IncludeMap, google3IncludeMapWeak + }; + + @SuppressWarnings("nls") + private static final IncludeMap symbolIncludeMap = new IncludeMap(false, false, new String[] { + "EOF", "", + "EOF", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "blkcnt_t", "", + "blkcnt_t", "", + "blksize_t", "", + "blksize_t", "", + "calloc", "", + "daddr_t", "", + "daddr_t", "", + "dev_t", "", + "dev_t", "", + "error_t", "", + "error_t", "", + "error_t", "", + "free", "", + "fsblkcnt_t", "", + "fsblkcnt_t", "", + "fsfilcnt_t", "", + "fsfilcnt_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "id_t", "", + "id_t", "", + "ino64_t", "", + "ino64_t", "", + "ino_t", "", + "ino_t", "", + "ino_t", "", + "int8_t", "", + "int8_t", "", + "intptr_t", "", + "intptr_t", "", + "key_t", "", + "key_t", "", + "malloc", "", + "mode_t", "", + "mode_t", "", + "mode_t", "", + "mode_t", "", + "nlink_t", "", + "nlink_t", "", + "off64_t", "", + "off64_t", "", + "off_t", "", + "off_t", "", + "off_t", "", + "off_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "realloc", "", + "sigset_t", "", + "sigset_t", "", + "sigset_t", "", + "size_t", "", + "socklen_t", "", + "socklen_t", "", + "socklen_t", "", + "ssize_t", "", + "ssize_t", "", + "ssize_t", "", + "ssize_t", "", + "std::allocator", "", + "std::allocator", "", + "std::allocator", "", + "std::allocator", "", + "std::allocator", "", + "std::char_traits", "", + "std::char_traits", "", + "std::char_traits", "", + "suseconds_t", "", + "suseconds_t", "", + "suseconds_t", "", + "u_char", "", + "u_char", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "useconds_t", "", + "useconds_t", "", + "va_list", "", + }); + + @SuppressWarnings("nls") + private static final IncludeMap cIncludeMap = new IncludeMap(true, false, new String[] { + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + }); + + private static final IncludeMap cIncludeMapWeak = new IncludeMap(false, false, new String[] { + }); + + @SuppressWarnings("nls") + private static final IncludeMap cppIncludeMap = new IncludeMap(true, true, new String[] { + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + }); + + @SuppressWarnings("nls") + private static final IncludeMap cppIncludeMapWeak = new IncludeMap(false, true, new String[] { + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + }); + + @SuppressWarnings("nls") + private static final IncludeMap google3IncludeMap = new IncludeMap(true, true, new String[] { + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "", "base/logging.h", + "base/vlog_is_on.h", "base/logging.h" + }); + + @SuppressWarnings("nls") + private static final IncludeMap google3IncludeMapWeak = new IncludeMap(false, true, new String[] { + "base/commandlineflags_declare.h", "base/commandlineflags.h" + }); + + private final InclusionContext fContext; + + private List fIncludeMaps; + + public HeaderSubstitutor(InclusionContext context) { + fContext = context; + } + + /** + * Selects the header file to be appear in the {@code #include} statement given the header file + * that needs to be included. Returns absolute path for the header if it can be uniquely + * determined, or {@code null} otherwise. The header is determined uniquely if there are no + * optional replacement headers for it, and there are no more that one forced replacement. + * @param path absolute path of the header to be included directly or indirectly + * @return absolute path of the header to be included directly + */ + public IPath getUniqueRepresentativeHeader(IPath path) { + IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(path); + if (includeInfo == null) + return null; + List maps = getAllIncludeMaps(); + for (IncludeMap map : maps) { + if (map.isForcedReplacement()) { + List replacements = map.getMapping(includeInfo); + if (replacements.size() == 1) { + includeInfo = replacements.get(0); + } else if (replacements.size() > 1) { + return null; + } + } + } + for (IncludeMap map : maps) { + if (!map.isForcedReplacement()) { + if (!map.getMapping(includeInfo).isEmpty()) { + return null; + } + } + } + return fContext.resolveInclude(includeInfo); + } + + public IPath getPreferredRepresentativeHeader(IPath path) { + IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(path); + if (includeInfo == null) + return path; + // TODO(sprigogin): Take symbolIncludeMap into account. + List candidates = new ArrayList(); + candidates.add(includeInfo); + List maps = getAllIncludeMaps(); + for (IncludeMap map : maps) { + for (int i = 0; i < candidates.size();) { + IncludeInfo candidate = candidates.get(i); + List replacements = map.getMapping(candidate); + int increment = 1; + if (!replacements.isEmpty()) { + if (map.isForcedReplacement()) { + candidates.remove(i); + increment = 0; + } + candidates.addAll(i, replacements); + increment += replacements.size(); + } + i += increment; + } + } + IPath firstResolved = null; + for (IncludeInfo candidate : candidates) { + IPath header = fContext.resolveInclude(candidate); + if (header != null) { + if (fContext.isIncluded(header)) + return header; + if (firstResolved == null) + firstResolved = header; + } + } + + return firstResolved != null ? firstResolved : path; + } + + /** + * Performs heuristic header substitution. + */ + public IPath getPreferredRepresentativeHeaderByHeuristic(InclusionRequest request) { + Set indexFiles = request.getDeclaringFiles().keySet(); + String symbolName = request.getBinding().getName(); + ArrayDeque front = new ArrayDeque(); + HashSet processed = new HashSet(); + + try { + // Look for headers without an extension and a matching name. + if (fContext.isCXXLanguage()) { + front.addAll(indexFiles); + processed.addAll(indexFiles); + + while (!front.isEmpty()) { + IIndexFile file = front.remove(); + + String path = IncludeUtil.getPath(file); + + if (!hasExtension(path) && getFilename(path).equalsIgnoreCase(symbolName)) { + // A C++ header without an extension and with a name which matches the name + // of the symbol which should be declared is a perfect candidate for inclusion. + return IndexLocationFactory.getAbsolutePath(file.getLocation()); + } + + // Process the next level of the include hierarchy. + IIndexInclude[] includes = fContext.getIndex().findIncludedBy(file, 0); + for (IIndexInclude include : includes) { + IIndexFile includer = include.getIncludedBy(); + if (!processed.contains(includer)) { + front.add(includer); + processed.add(includer); + } + } + } + } + + // Repeat the process, this time only looking for headers without an extension. + front.clear(); + front.addAll(indexFiles); + processed.clear(); + processed.addAll(indexFiles); + + while (!front.isEmpty()) { + IIndexFile file = front.remove(); + + String path = IncludeUtil.getPath(file); + + if (fContext.isCXXLanguage() && !hasExtension(path)) { + // A C++ header without an extension is still a very good candidate for inclusion. + return IndexLocationFactory.getAbsolutePath(file.getLocation()); + } + + // Process the next level of the include hierarchy. + IIndexInclude[] includes = fContext.getIndex().findIncludedBy(file, 0); + for (IIndexInclude include : includes) { + IIndexFile includer = include.getIncludedBy(); + if (!processed.contains(includer)) { + URI uri = includer.getLocation().getURI(); + if (IncludeUtil.isSource(includer, fContext.getProject()) || isWorkspaceFile(uri)) { + return IndexLocationFactory.getAbsolutePath(file.getLocation()); + } + front.add(includer); + processed.add(includer); + } + } + } + } catch (CoreException e) { + CUIPlugin.log(e); + } + + return request.getCandidatePaths().iterator().next(); + } + + private List getAllIncludeMaps() { + if (fIncludeMaps == null) { + fIncludeMaps = new ArrayList(); + for (IncludeMap map : INCLUDE_MAPS) { + if (fContext.isCXXLanguage() || !map.isCppOnly()) + fIncludeMaps.add(map); + } + } + return fIncludeMaps; + } + + /** + * Returns whether the given URI is an URI within the workspace + * @param uri + * @return + */ + private static boolean isWorkspaceFile(URI uri) { + for (IFile file : ResourceLookup.findFilesForLocationURI(uri)) { + if (file.exists()) { + return true; + } + } + return false; + } + + /** + * Returns whether the given path has a file suffix, or not. + * @param path + * @return + */ + private static boolean hasExtension(String path) { + return path.indexOf('.', path.lastIndexOf('/') + 1) >= 0; + } + + /** + * Returns the filename of the given path, without extension. + * @param path + * @return + */ + private static String getFilename(String path) { + int startPos = path.lastIndexOf('/') + 1; + int endPos = path.lastIndexOf('.'); + if (endPos > startPos) { + return path.substring(startPos, endPos); + } else { + return path.substring(startPos); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java new file mode 100644 index 00000000000..f1b4ce1f179 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2012 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.refactoring.includes; + +public class IncludeInfo { + private final String name; + private final boolean isSystem; + + public IncludeInfo(String name, boolean isSystem) { + if (name == null || name.isEmpty()) + throw new IllegalArgumentException(); + this.name = name; + this.isSystem = isSystem; + } + + public IncludeInfo(String includeText) { + if (includeText == null || includeText.isEmpty()) + throw new IllegalArgumentException(); + boolean isSystem = false; + int begin = 0; + switch (includeText.charAt(0)) { + case '<': + isSystem = true; + //$FALL-THROUGH$ + case '"': + ++begin; + break; + } + int end = includeText.length(); + switch (includeText.charAt(end - 1)) { + case '>': + case '"': + --end; + break; + } + if (begin >= end) + throw new IllegalArgumentException(); + + this.name = includeText.substring(begin, end); + this.isSystem = isSystem; + } + + public final String getName() { + return name; + } + + public final boolean isSystem() { + return isSystem; + } + + @Override + public int hashCode() { + return name.hashCode() * 31 + (isSystem ? 1 : 0); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + IncludeInfo other = (IncludeInfo) obj; + return name.equals(other.name) && isSystem == other.isSystem; + } + + /** + * Returns the include string as it appears in an {@code #include} statement. + */ + @Override + public String toString() { + return (isSystem ? '<' : '"') + name + (isSystem ? '>' : '"'); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java new file mode 100644 index 00000000000..6fe6e451e94 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2012 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.refactoring.includes; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class IncludeMap { + private final boolean forcedReplacement; + private final Map> map; + private final boolean cppOnly; + + public IncludeMap(boolean forcedReplacement, boolean cppOnly) { + this.forcedReplacement = forcedReplacement; + this.cppOnly = cppOnly; + this.map = new HashMap>(); + } + + /** + * @param keysAndValues an array of keys and values: [key1, value1, key2, value2, ...]. + * Keys and values may be optionally surrounded by double quotes or angle brackets. + * Angle brackets indicate a system include. + */ + public IncludeMap(boolean forcedReplacement, boolean cppOnly, String[] keysAndValues) { + if (keysAndValues.length % 2 != 0) + throw new IllegalArgumentException("More keys than values"); //$NON-NLS-1$ + this.forcedReplacement = forcedReplacement; + this.cppOnly = cppOnly; + this.map = new HashMap>(keysAndValues.length / 2); + for (int i = 0; i < keysAndValues.length;) { + String key = keysAndValues[i++]; + addMapping(key, keysAndValues[i++]); + } + } + + /** + * Indicates that header file {@code to} should be used instead of {@code from}. + * + * @param from The header file to be replaced. + * @param to The header file to be used. + */ + protected void addMapping(IncludeInfo from, IncludeInfo to) { + List list = map.get(from); + if (list == null) { + list = new ArrayList(2); + map.put(from, list); + } + list.add(to); + } + + /** + * Indicates that header file {@code to} should be used instead of {@code from}. + + * @param from The header file to be replaced. The header is represented by an include name + * optionally surrounded by double quotes or angle brackets. Angle brackets indicate + * a system include. + * @param to The header file to be used. The header is represented by an include name optionally + * surrounded by double quotes or angle brackets. Angle brackets indicate a system include. + */ + public void addMapping(String from, String to) { + addMapping(new IncludeInfo(from), new IncludeInfo(to)); + } + + /** + * Returns header files that should be used instead of the given one. + * + * @param from The header file to be replaced. A system header has to match exactly. + * A non-system header matches both, non-system and system headers. + * @return The list of header files ordered by decreasing preference. + */ + public List getMapping(IncludeInfo from) { + List list = map.get(from); + if (list == null) { + if (!from.isSystem()) + list = map.get(new IncludeInfo(from.getName(), true)); + if (list == null) + return Collections.emptyList(); + } + return list; + } + + /** + * Returns header files that should be used instead of the given one. + * + * @param from The header file to be replaced. A system header has to match exactly. + * A non-system header matches both, non-system and system headers. + * @return The list of header files ordered by decreasing preference. + */ + public List getMapping(String from) { + return getMapping(new IncludeInfo(from)); + } + + public IncludeInfo getPreferredMapping(IncludeInfo from) { + List list = getMapping(from); + return list == null ? null : list.get(0); + } + + public boolean isForcedReplacement() { + return forcedReplacement; + } + + public boolean isCppOnly() { + return cppOnly; + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java new file mode 100644 index 00000000000..d3f97490b00 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java @@ -0,0 +1,704 @@ +/******************************************************************************* + * Copyright (c) 2012 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 + * Mathias Kunter + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.includes; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +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.ui.dialogs.ElementListSelectionDialog; +import org.eclipse.ui.texteditor.ITextEditor; + +import org.eclipse.cdt.core.dom.IName; +import org.eclipse.cdt.core.dom.ast.DOMException; +import org.eclipse.cdt.core.dom.ast.EScopeKind; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.ICompositeType; +import org.eclipse.cdt.core.dom.ast.IEnumeration; +import org.eclipse.cdt.core.dom.ast.IFunction; +import org.eclipse.cdt.core.dom.ast.IFunctionType; +import org.eclipse.cdt.core.dom.ast.IParameter; +import org.eclipse.cdt.core.dom.ast.IScope; +import org.eclipse.cdt.core.dom.ast.IType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexFile; +import org.eclipse.cdt.core.index.IIndexFileLocation; +import org.eclipse.cdt.core.index.IIndexFileSet; +import org.eclipse.cdt.core.index.IIndexInclude; +import org.eclipse.cdt.core.index.IIndexName; +import org.eclipse.cdt.core.index.IndexLocationFactory; +import org.eclipse.cdt.core.model.IBuffer; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.utils.PathUtil; + +import org.eclipse.cdt.internal.core.resources.PathCanonicalizationStrategy; + +import org.eclipse.cdt.internal.ui.editor.CEditorMessages; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences.IncludeType; + +/** + * Organizes the include directives and forward declarations of a source or header file. + */ +public class IncludeOrganizer { + // TODO(sprigogin): Move to a preference. + private static final String[] PARTNER_FILE_SUFFIXES = { "test", "unittest" }; //$NON-NLS-1$//$NON-NLS-2$ + + private final ITextEditor fEditor; + private final InclusionContext fContext; + + /** + * Constructor + * @param editor The editor on which this organize includes action should operate. + * @param index + */ + public IncludeOrganizer(ITextEditor editor, ITranslationUnit tu, IIndex index) { + fEditor = editor; + fContext = new InclusionContext(tu, index); + } + + /** + * Organizes the includes for a given translation unit. + * @param ast The AST translation unit to process. + * @throws CoreException + */ + public void organizeIncludes(IASTTranslationUnit ast) throws CoreException { + // Process the given translation unit with the inclusion resolver. + BindingClassifier resolver = new BindingClassifier(fContext, ast); + Set bindingsToDefine = resolver.getBindingsToDefine(); + + HeaderSubstitutor headerSubstitutor = new HeaderSubstitutor(fContext); + // Create the list of header files which have to be included by examining the list of + // bindings which have to be defined. + IIndexFileSet reachableHeaders = ast.getIndexFileSet(); + + List requests = createInclusionRequests(bindingsToDefine, reachableHeaders); + processInclusionRequests(requests, headerSubstitutor); + + // Stores the forward declarations for composite types and enumerations as text. + List forwardDeclarations = new ArrayList(); + + // Stores the forward declarations for C-style functions as text. + List functionForwardDeclarations = new ArrayList(); + + // Create the forward declarations by examining the list of bindings which have to be + // declared. + Set bindings = removeBindingsDefinedInIncludedHeaders(resolver.getBindingsToDeclare(), reachableHeaders); + for (IBinding binding : bindings) { + // Create the text of the forward declaration of this binding. + StringBuilder declarationText = new StringBuilder(); + + // Consider the namespace(s) of the binding. + List scopeNames = new ArrayList(); + try { + IScope scope = binding.getScope(); + while (scope != null && scope.getKind() == EScopeKind.eNamespace) { + IName scopeName = scope.getScopeName(); + if (scopeName != null) { + scopeNames.add(scopeName); + } + scope = scope.getParent(); + } + } catch (DOMException e) { + } + Collections.reverse(scopeNames); + for (IName scopeName : scopeNames) { + declarationText.append("namespace "); //$NON-NLS-1$ + declarationText.append(scopeName.toString()); + declarationText.append(" { "); //$NON-NLS-1$ + } + + // Initialize the list which should be used to store the declaration. + List forwardDeclarationListToUse = forwardDeclarations; + + // Check the type of the binding and create a corresponding forward declaration text. + if (binding instanceof ICompositeType) { + // Forward declare a composite type. + ICompositeType compositeType = (ICompositeType) binding; + + // Check whether this is a template type. + ICPPTemplateDefinition templateDefinition = null; + if (compositeType instanceof ICPPTemplateDefinition) { + templateDefinition = (ICPPTemplateDefinition) compositeType; + } else if (compositeType instanceof ICPPTemplateInstance) { + templateDefinition = ((ICPPTemplateInstance) compositeType).getTemplateDefinition(); + } + if (templateDefinition != null) { + // Create the template text. + declarationText.append("template "); //$NON-NLS-1$ + ICPPTemplateParameter[] templateParameters = templateDefinition.getTemplateParameters(); + for (int i = 0; i < templateParameters.length; i++) { + ICPPTemplateParameter templateParameter = templateParameters[i]; + if (i == 0) { + declarationText.append("<"); //$NON-NLS-1$ + } + declarationText.append("typename "); //$NON-NLS-1$ + declarationText.append(templateParameter.getName()); + if (i != templateParameters.length - 1) { + declarationText.append(", "); //$NON-NLS-1$ + } + } + if (templateParameters.length > 0) { + declarationText.append("> "); //$NON-NLS-1$ + } + } + + // Append the corresponding keyword. + switch (compositeType.getKey()) { + case ICPPClassType.k_class: + declarationText.append("class"); //$NON-NLS-1$ + break; + case ICompositeType.k_struct: + declarationText.append("struct"); //$NON-NLS-1$ + break; + case ICompositeType.k_union: + declarationText.append("union"); //$NON-NLS-1$ + break; + } + + // Append the name of the composite type. + declarationText.append(' '); + declarationText.append(binding.getName()); + + // Append the semicolon. + declarationText.append(';'); + } else if (binding instanceof IEnumeration) { + // Forward declare an enumeration class (C++11 syntax). + declarationText.append("enum class "); //$NON-NLS-1$ + declarationText.append(binding.getName()); + declarationText.append(';'); + } else if (binding instanceof IFunction && !(binding instanceof ICPPMethod)) { + // Forward declare a C-style function. + IFunction function = (IFunction) binding; + + // Append return type and function name. + IFunctionType functionType = function.getType(); + // TODO(sprigogin) Improper use of IType.toString(); + declarationText.append(functionType.getReturnType().toString()); + declarationText.append(' '); + declarationText.append(function.getName()); + declarationText.append('('); + + // Append parameter types and names. + IType[] parameterTypes = functionType.getParameterTypes(); + IParameter[] parameters = function.getParameters(); + for (int i = 0; i < parameterTypes.length && i < parameters.length; i++) { + // TODO(sprigogin) Improper use of IType.toString(); + declarationText.append(parameterTypes[i].toString()); + char lastChar = declarationText.charAt(declarationText.length() - 1); + if (lastChar != '*' && lastChar != '&') { + // Append a space to separate the type name from the parameter name. + declarationText.append(' '); + } + declarationText.append(parameters[i].getName()); + if (i != parameterTypes.length - 1 && i != parameters.length - 1) { + declarationText.append(", "); //$NON-NLS-1$ + } + } + + declarationText.append(");"); //$NON-NLS-1$ + + // Add this forward declaration to the separate function forward declaration list. + forwardDeclarationListToUse = functionForwardDeclarations; + } else { + // We don't handle forward declarations for those types of bindings. Ignore it. + continue; + } + + // Append the closing curly brackets from the namespaces (if any). + for (int i = 0; i < scopeNames.size(); i++) { + declarationText.append(" }"); //$NON-NLS-1$ + } + + // Add the forward declaration to the corresponding list. + forwardDeclarationListToUse.add(declarationText.toString()); + } + + // Obtain the final lists of library, project, and relative headers. + List relativeIncludeDirectives = new ArrayList(); + List projectIncludeDirectives = new ArrayList(); + List libraryIncludeDirectives = new ArrayList(); + List allIncludeDirectives = new ArrayList(); + IncludePreferences preferences = fContext.getPreferences(); + for (IPath file : fContext.getHeadersToInclude()) { + if (preferences.allowReordering && preferences.sortByHeaderLocation) { + // Add the created include directives to different lists. + createIncludeDirective(file, relativeIncludeDirectives, projectIncludeDirectives, libraryIncludeDirectives); + } else { + // Add all created include directives to the same list, making sure that no sort + // order is applied. + createIncludeDirective(file, allIncludeDirectives, allIncludeDirectives, allIncludeDirectives); + } + } + + // Create the source code to insert into the editor. + IBuffer fBuffer = fContext.getTranslationUnit().getBuffer(); + String lineSep = getLineSeparator(fBuffer); + String insertText = new String(); + + if (preferences.allowReordering) { + if (preferences.removeUnusedIncludes) { + // Remove *all* existing includes and forward declarations. Those which are required + // will be added again right afterwards. + + // TODO implement this + } + + if (preferences.sortByHeaderLocation) { + // Sort by header file location. + + // Process the different types of include directives separately. + for (IncludeType includeType : preferences.groupOrder) { + List stringList = null; + + if (includeType == IncludeType.RELATIVE_HEADER) { + stringList = relativeIncludeDirectives; + } else if (includeType == IncludeType.PROJECT_HEADER) { + stringList = projectIncludeDirectives; + } else if (includeType == IncludeType.LIBRARY_HEADER) { + stringList = libraryIncludeDirectives; + } else if (includeType == IncludeType.FORWARD_DECLARATION) { + stringList = forwardDeclarations; + } else if (includeType == IncludeType.FUNCTION_FORWARD_DECLARATION) { + stringList = functionForwardDeclarations; + } + if (stringList == null || stringList.isEmpty()) { + continue; + } + + // Sort alphabetically + if (preferences.sortAlphabetically) { + Collections.sort(stringList); + } + + // Insert the actual text. + for (String str : stringList) { + insertText += str + lineSep; + } + + // Insert blank line + if (preferences.separateIncludeBlocks) { + insertText += lineSep; + } + } + } else { + // Don't sort by header file location. + + // Sort alphabetically + if (preferences.sortAlphabetically) { + Collections.sort(allIncludeDirectives); + } + + // Insert the actual text. + for (String str : allIncludeDirectives) { + insertText += str + lineSep; + } + } + } else { + // Existing include directives must not be reordered. + + // Compare the list of existing include directives with the list of required include directives. + // TODO: Implement this. The following code template may be used for that: + /*for (IInclude includeDirective : fTu.getIncludes()) { + if (!allIncludeDirectives.contains(includeDirective)) { + // This include directive from the editor isn't present within the list of required includes and is therefore unused. + // Remove it from the editor, if enabled within the preferences. + if (OrganizeIncludesPreferences.getPreferenceStore().getBoolean(PREF_REMOVE_UNUSED_INCLUDES)) { + removeIncludeDirective(fTu, includeDirective); + } + } else { + // This include directive from the editor is required. Remove it from the list of required includes. + allIncludeDirectives.remove(includeDirective); + } + }*/ + + // Insert those includes which still remain within the list of required includes (i.e. those include directives which have + // been added now). + for (String str : allIncludeDirectives) { + insertText += str + lineSep; + } + + // Insert forward declarations. + for (String str : forwardDeclarations) { + insertText += str + lineSep; + } + } + + if (!insertText.isEmpty()) { + // Insert the text plus a separating blank line into the editor. + insertText = insertText.trim() + lineSep + lineSep; + fBuffer.replace(0, 0, insertText); + } + } + + private Set removeBindingsDefinedInIncludedHeaders(Set bindings, + IIndexFileSet reachableHeaders) throws CoreException { + Set filteredBindings = new HashSet(bindings); + + List requests = createInclusionRequests(bindings, reachableHeaders); + Set allIncludedHeaders = new HashSet(); + allIncludedHeaders.addAll(fContext.getHeadersAlreadyIncluded()); + allIncludedHeaders.addAll(fContext.getHeadersToInclude()); + + for (InclusionRequest request : requests) { + if (isSatisfiedByIncludedHeaders(request, allIncludedHeaders)) + filteredBindings.remove(request.getBinding()); + } + return filteredBindings; + } + + protected boolean isSatisfiedByIncludedHeaders(InclusionRequest request, Set includedHeaders) + throws CoreException { + for (IIndexFile file : request.getDeclaringFiles().keySet()) { + IIndexInclude[] includedBy = fContext.getIndex().findIncludedBy(file, IIndex.DEPTH_INFINITE); + for (IIndexInclude include : includedBy) { + IPath path = getPath(include.getIncludedByLocation()); + if (includedHeaders.contains(path)) { + return true; + } + } + } + return false; + } + + private void processInclusionRequests(List requests, + HeaderSubstitutor headerSubstitutor) { + // Add partner header if necessary. + HashSet partnerIndexFiles = new HashSet(); + for (InclusionRequest request : requests) { + List candidatePaths = request.getCandidatePaths(); + if (candidatePaths.size() == 1) { + IPath path = candidatePaths.iterator().next(); + if (isPartnerFile(path)) { + request.resolve(path); + fContext.addHeaderToInclude(path); + try { + IIndexFile indexFile = request.getDeclaringFiles().keySet().iterator().next(); + if (!partnerIndexFiles.contains(indexFile)) { + for (IIndexInclude include : indexFile.getIncludes()) { + fContext.addHeaderAlreadyIncluded(getPath(include.getIncludesLocation())); + } + partnerIndexFiles.add(indexFile); + } + } catch (CoreException e) { + CUIPlugin.log(e); + } + } + } + } + + // Process headers that are either indirectly included or have unique representatives. + for (InclusionRequest request : requests) { + if (!request.isResolved()) { + List candidatePaths = request.getCandidatePaths(); + Set representativeHeaders = new HashSet(); + boolean allRepresented = true; + for (IPath path : candidatePaths) { + if (fContext.isIncluded(path)) { + request.resolve(path); + break; + } else { + IPath header = headerSubstitutor.getUniqueRepresentativeHeader(path); + if (header != null) { + representativeHeaders.add(header); + } else { + allRepresented = false; + } + } + } + + if (!request.isResolved() && allRepresented && representativeHeaders.size() == 1) { + IPath path = representativeHeaders.iterator().next(); + request.resolve(path); + if (!fContext.isAlreadyIncluded(path)) + fContext.addHeaderToInclude(path); + } + } + } + + // Process remaining unambiguous inclusion requests. + for (InclusionRequest request : requests) { + if (!request.isResolved()) { + List candidatePaths = request.getCandidatePaths(); + if (candidatePaths.size() == 1) { + IPath path = candidatePaths.iterator().next(); + if (fContext.isIncluded(path)) { + request.resolve(path); + } else { + IPath header = headerSubstitutor.getPreferredRepresentativeHeader(path); + if (header.equals(path) && fContext.getPreferences().heuristicHeaderSubstitution) { + header = headerSubstitutor.getPreferredRepresentativeHeaderByHeuristic(request); + } + request.resolve(header); + if (!fContext.isAlreadyIncluded(header)) + fContext.addHeaderToInclude(header); + } + } + } + } + + // Resolve ambiguous inclusion requests. + + // Maps a set of header files presented to the user to the file selected by the user. + HashMap, IPath> userChoiceCache = new HashMap, IPath>(); + + for (InclusionRequest request : requests) { + if (!request.isResolved()) { + List candidatePaths = request.getCandidatePaths(); + for (IPath path : candidatePaths) { + if (fContext.isIncluded(path)) { + request.resolve(path); + break; + } + } + IPath header = askUserToSelectHeader(request.getBinding(), candidatePaths, userChoiceCache); + request.resolve(header); + if (!fContext.isAlreadyIncluded(header)) + fContext.addHeaderToInclude(header); + } + } + } + + private IPath getPath(IIndexFileLocation location) { + return IndexLocationFactory.getAbsolutePath(location); + } + + /** + * Checks if the given path points to a partner header of the current translation unit. + * A header is considered a partner if its name without extension is the same as the name of + * the translation unit, or the name of the translation unit differs by one of the suffixes + * used for test files. + */ + private boolean isPartnerFile(IPath path) { + String headerName = path.removeFileExtension().lastSegment(); + String sourceName = fContext.getTranslationUnit().getLocation().removeFileExtension().lastSegment(); + if (headerName.equals(sourceName)) + return true; + if (sourceName.startsWith(headerName)) { + int pos = headerName.length(); + while (pos < sourceName.length() && !Character.isLetterOrDigit(sourceName.charAt(pos))) { + pos++; + } + if (pos == sourceName.length()) + return true; + String suffix = sourceName.substring(pos); + for (String s : PARTNER_FILE_SUFFIXES) { + if (suffix.equalsIgnoreCase(s)) + return true; + } + } + return false; + } + + private List createInclusionRequests(Set bindingsToDefine, + IIndexFileSet reachableHeaders) throws CoreException { + List requests = new ArrayList(bindingsToDefine.size()); + IIndex index = fContext.getIndex(); + + binding_loop: for (IBinding binding : bindingsToDefine) { + IIndexName[] indexNames; + if (binding instanceof IFunction) { + // For functions we need to include the declaration. + indexNames = index.findDeclarations(binding); + } else { + // For all other bindings we need to include the definition. + indexNames = index.findDefinitions(binding); + } + + if (indexNames.length != 0) { + // Check whether the index name is (also) present within the current file. + // If yes, we don't need to include anything. + for (IIndexName indexName : indexNames) { + IIndexFile indexFile = indexName.getFile(); + if (indexFile.getLocation().getURI().equals(fContext.getTranslationUnit().getLocationURI())) { + continue binding_loop; + } + } + + Map declaringHeaders = new HashMap(); + Map reachableDeclaringHeaders = new HashMap(); + for (IIndexName indexName : indexNames) { + IIndexFile indexFile = indexName.getFile(); + if (IncludeUtil.isSource(indexFile, fContext.getProject()) && + index.findIncludedBy(indexFile, 0).length == 0) { + // The target is a source file which isn't included by any other files. + // Don't include it. + continue; + } + IPath path = getPath(indexFile.getLocation()); + declaringHeaders.put(indexFile, path); + if (reachableHeaders.contains(indexFile)) + reachableDeclaringHeaders.put(indexFile, path); + } + + if (!declaringHeaders.isEmpty()) { + boolean reachable = false; + if (!reachableDeclaringHeaders.isEmpty()) { + reachable = true; + declaringHeaders = reachableDeclaringHeaders; + } + requests.add(new InclusionRequest(binding, declaringHeaders, reachable)); + } + } + } + return requests; + } + + /** + * Returns the line separator for the given buffer. + * @param fBuffer + * @return + */ + private String getLineSeparator(IBuffer fBuffer) { + try { + if (fBuffer instanceof IAdaptable) { + IDocument doc= (IDocument) ((IAdaptable) fBuffer).getAdapter(IDocument.class); + if (doc != null) { + String delim= doc.getLineDelimiter(0); + if (delim != null) { + return delim; + } + } + } + } catch (BadLocationException e) { + } + return System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * Picks a suitable header file that should be used to include the given binding. + * + * @param binding The binding which should be resolved. + * @param headers The available header files to pick from. + * @param userChoiceCache the cache of previous user choices + * @return The chosen header file. + */ + private IPath askUserToSelectHeader(IBinding binding, Collection headers, + HashMap, IPath> userChoiceCache) { + if (headers.isEmpty()) + return null; + if (headers.size() == 1) + return headers.iterator().next(); + + // Check the decision cache. If the cache doesn't help, ask the user. + // Query the cache. + if (userChoiceCache.containsKey(headers)) { + return userChoiceCache.get(headers); + } + + // Ask the user. + final IPath[] elemArray = headers.toArray(new IPath[headers.size()]); + final IPath[] selectedElement = new IPath[1]; + final String bindingName = binding.getName(); + runInUIThread(new Runnable() { + @Override + public void run() { + ElementListSelectionDialog dialog = + new ElementListSelectionDialog(fEditor.getSite().getShell(), new LabelProvider()); + dialog.setElements(elemArray); + dialog.setTitle(CEditorMessages.OrganizeIncludes_label); + dialog.setMessage(NLS.bind(Messages.IncludeOrganizer_ChooseHeader, bindingName)); + if (dialog.open() == Window.OK) { + selectedElement[0] = (IPath) dialog.getFirstResult(); + } + } + }); + + IPath selectedHeader = selectedElement[0]; + + if (selectedHeader == null) + throw new OperationCanceledException(); + + 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); + } + } + + /** + * Adds an include directive. + * @param headerFile The header file which should be included. + * @param relativeIncludeDirectives Out parameter. The list of relative headers to include. + * @param projectIncludeDirectives Out parameter. The list of project headers to include. + * @param libraryIncludeDirectives Out parameter. The list of library headers to include. + * @throws CoreException + */ + private void createIncludeDirective(IPath headerFile, + Collection relativeIncludeDirectives, + Collection projectIncludeDirectives, + Collection libraryIncludeDirectives) throws CoreException { + IPath targetLocation = headerFile; + IPath targetDirectory = targetLocation.removeLastSegments(1); + targetDirectory = new Path(PathCanonicalizationStrategy.getCanonicalPath(targetDirectory.toFile())); + IPath sourceDirectory = fContext.getCurrentDirectory(); + sourceDirectory = new Path(PathCanonicalizationStrategy.getCanonicalPath(sourceDirectory.toFile())); + + IncludePreferences preferences = fContext.getPreferences(); + boolean relativeToSource = false; + if (preferences.relativeHeaderInSameDir && + PathUtil.equalPath(sourceDirectory, targetDirectory)) { + // The header is located within the same directory as the source file. + relativeToSource = true; + } else if (preferences.relativeHeaderInSubdir && + PathUtil.isPrefix(sourceDirectory, targetLocation)) { + // The header is located within a subdirectory of the source file's directory. + relativeToSource = true; + } else if (preferences.relativeHeaderInParentDir && + PathUtil.isPrefix(targetDirectory, sourceDirectory)) { + // The header is located within a parent directory of the source file's directory. + relativeToSource = true; + } + + IncludeInfo includeInfo = null; + if (!relativeToSource) + includeInfo = fContext.getIncludeForHeaderFile(targetLocation); + Collection headerList; + if (includeInfo != null) { + headerList = includeInfo.isSystem() ? libraryIncludeDirectives : projectIncludeDirectives; + } else { + // Include the header relative to the source file. + includeInfo = new IncludeInfo(PathUtil.makeRelativePath(targetLocation, sourceDirectory).toPortableString()); + // Add this header to the relative headers. + headerList = relativeIncludeDirectives; + } + + headerList.add("#include " + includeInfo.toString()); //$NON-NLS-1$ + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java new file mode 100644 index 00000000000..37513bd3574 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java @@ -0,0 +1,319 @@ +/******************************************************************************* + * Copyright (c) 2012 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.refactoring.includes; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jface.preference.IPreferenceStore; + +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.ui.PreferenceConstants; + +/** + * Preferences for managing of includes. + */ +public class IncludePreferences { + public final boolean allowReordering; + public final boolean forwardDeclareCompositeTypes; + public final boolean forwardDeclareEnums; + public final boolean forwardDeclareFunctions; + public final boolean forwardDeclareNamespaceElements; + public final boolean relativeHeaderInSameDir; + public final boolean relativeHeaderInSubdir; + public final boolean relativeHeaderInParentDir; + public final boolean heuristicHeaderSubstitution; + public final boolean separateIncludeBlocks; + public final boolean sortAlphabetically; + public final boolean removeUnusedIncludes; + public final boolean commentOutUnusedIncludes; + public final boolean sortByHeaderLocation; + public final List groupOrder; // TODO(sprigogin): Change to IncludeGroupOrder + + public static enum IncludeGroupType { + SIBLING_HEADER, HEADERS_IN_SAME_FOLDER, HEADERS_IN_SUBFOLDERS, + SYSTEM_HEADERS, SYSTEM_C_HEADERS, SYSTEM_CPP_HEADERS, + PROJECT_HEADERS, EXTERNAL_HEADERS, SPECIAL_HEADERS, + TYPE_FORWARD_DECLARATIONS, FUNCTION_FORWARD_DECLARATIONS, USING_DECLARATIONS + } + + public static class GroupStyle { + public boolean useRelativePath; + public boolean useAngleBrackets; + public boolean separateByBlankLine; + } + + public IncludePreferences(ICProject project) { + forwardDeclareCompositeTypes = PreferenceConstants.getPreference( + PREF_FORWARD_DECLARE_COMPOSITE_TYPES, project, true); + forwardDeclareEnums = PreferenceConstants.getPreference( + PREF_FORWARD_DECLARE_ENUMS, project, false); + forwardDeclareFunctions = PreferenceConstants.getPreference( + PREF_FORWARD_DECLARE_FUNCTIONS, project, false); + forwardDeclareNamespaceElements = PreferenceConstants.getPreference( + PREF_FORWARD_DECLARE_NAMESPACE_ELEMENTS, project, true); + + // Relative headers preferences + relativeHeaderInSameDir = PreferenceConstants.getPreference( + PREF_RELATIVE_HEADER_IN_SAME_DIR, project, false); + relativeHeaderInSubdir = PreferenceConstants.getPreference( + PREF_RELATIVE_HEADER_IN_SUB_DIR, project, false); + relativeHeaderInParentDir = PreferenceConstants.getPreference( + PREF_RELATIVE_HEADER_IN_PARENT_DIR, project, false); + + // Header resolution preferences + heuristicHeaderSubstitution = PreferenceConstants.getPreference( + PREF_HEURISTIC_HEADER_SUBSTITUTION, project, true); + + // Header sort order preferences + allowReordering = PreferenceConstants.getPreference( + PREF_ALLOW_TO_REORDER_INCLUDES, project, true); + sortByHeaderLocation = PreferenceConstants.getPreference( + PREF_SORT_BY_HEADER_LOCATION, project, true); + String order = PreferenceConstants.getPreference( + PREF_HEADER_LOCATION_SORT_ORDER, project, + IncludeType.RELATIVE_HEADER.toString() + ',' + + IncludeType.LIBRARY_HEADER.toString() + ',' + + IncludeType.PROJECT_HEADER.toString() + ',' + + IncludeType.FORWARD_DECLARATION.toString() + ',' + + IncludeType.FUNCTION_FORWARD_DECLARATION.toString()); + String[] textSortOrder = order.split(","); //$NON-NLS-1$ + List list = new ArrayList(textSortOrder.length); + for (String type : textSortOrder) { + list.add(IncludeType.valueOf(type)); + } + groupOrder = Collections.unmodifiableList(list); + + separateIncludeBlocks = PreferenceConstants.getPreference( + PREF_SEPARATE_INCLUDE_BLOCKS, project, true); + sortAlphabetically = PreferenceConstants.getPreference( + PREF_SORT_ALPHABETICALLY, project, true); + + // Unused include handling preferences + removeUnusedIncludes = PreferenceConstants.getPreference( + PREF_REMOVE_UNUSED_INCLUDES, project, false); + commentOutUnusedIncludes = PreferenceConstants.getPreference( + PREF_COMMENT_OUT_UNUSED_INCLUDES, project, true); + } + + // TODO(sprigogin): Move the constants and defaults to PreferenceConstants. + + /** + * Enumerates the different types of code constructs which the organize includes action can + * generate. + */ + public enum IncludeType { + /** + * A header which is located within the current file's directory. + */ + RELATIVE_HEADER, + + /** + * A header which is located within the current file's project directory. + */ + PROJECT_HEADER, + + /** + * A (library) header which is located outside of the current file's project directory. + */ + LIBRARY_HEADER, + + /** + * A forward declaration. + */ + FORWARD_DECLARATION, + + /** + * A forward declaration of a function. + */ + FUNCTION_FORWARD_DECLARATION, + + /** + * A problem like e.g. an unresolved symbol. + */ + FOUND_PROBLEM + } + + /** + * Enumerates the different options for having a protection against multiple header file + * inclusion. + */ + public enum MultipleInclusionProtectionType { + /** + * No protection against multiple header file inclusion. + */ + NONE, + + /** + * Use include guards to avoid multiple header file inclusion. + */ + INCLUDE_GUARDS, + + /** + * Use pragma once to avoid multiple header file inclusion. + */ + PRAGMA_ONCE + } + + /** + * Whether composite types should be forward declared if possible. + * + * Examples: + * class X; + * struct Y; + * union Z; + */ + public static final String PREF_FORWARD_DECLARE_COMPOSITE_TYPES = "forward_declare_composite_types"; //$NON-NLS-1$ + + /** + * Whether C++11-style enums should be forward declared if possible. + * + * Example: + * enum class X; + */ + public static final String PREF_FORWARD_DECLARE_ENUMS = "forward_declare_enums"; //$NON-NLS-1$ + + /** + * Whether C-style functions should be forward declared if possible. + * + * Example: + * void foo(); + */ + public static final String PREF_FORWARD_DECLARE_FUNCTIONS = "forward_declare_functions"; //$NON-NLS-1$ + + /** + * Whether elements nested within namespaces should be forward declared if possible. + * + * Examples: + * namespace N { class X; } + */ + public static final String PREF_FORWARD_DECLARE_NAMESPACE_ELEMENTS = "forward_declare_namespace_elements"; //$NON-NLS-1$ + + /** + * Whether headers located within the same directory as the source file should always + * be included relative to the source file. + */ + public static final String PREF_RELATIVE_HEADER_IN_SAME_DIR = "relative_header_in_same_dir"; //$NON-NLS-1$ + + /** + * Whether headers located within a subdirectory of the source file's directory should always + * be included relative to the source file. + */ + public static final String PREF_RELATIVE_HEADER_IN_SUB_DIR = "relative_header_in_sub_dir"; //$NON-NLS-1$ + + /** + * Whether headers located within a parent directory of the source file's directory should + * always be included relative to the source file. + */ + public static final String PREF_RELATIVE_HEADER_IN_PARENT_DIR = "relative_header_in_parent_dir"; //$NON-NLS-1$ + + /** + * Whether a heuristic approach should be used to resolve C++ header files. The heuristic + * prefers headers which have no file extension and / or are named like the symbol which should + * be defined. This often works out nicely since it's a commonly used naming convention for C++ + * (library) headers. + */ + public static final String PREF_HEURISTIC_HEADER_SUBSTITUTION = "heuristic_header_resolution"; //$NON-NLS-1$ + + /** + * Whether it's allowed to reorder existing include directives. If this is set to false, + * the original order is kept as far as possible. This may be necessary to avoid breaking code + * which makes assumptions about the order of the include directives. If this is set to true, + * a different sort order can be applied. Also see the other sort order preferences for further + * details. + */ + public static final String PREF_ALLOW_TO_REORDER_INCLUDES = "allow_to_reorder_includes"; //$NON-NLS-1$ + + /** + * Whether the include directives should be sorted by header file location. Ignored if + * PREF_ALLOW_TO_REORDER_INCLUDES is false. + */ + public static final String PREF_SORT_BY_HEADER_LOCATION = "sort_by_header_location"; //$NON-NLS-1$ + + /** + * Defines the header location sort order. Ignored if either PREF_ALLOW_TO_REORDER_INCLUDES or + * PREF_SORT_BY_HEADER_LOCATION is false. An example location sort order would be: + * Relative headers > Project headers > Library headers > Forward declarations + */ + public static final String PREF_HEADER_LOCATION_SORT_ORDER = "header_location_sort_order"; //$NON-NLS-1$ + + /** + * Whether the different blocks of include directives should be separated by a blank line. + * Ignored if either PREF_ALLOW_TO_REORDER_INCLUDES or PREF_SORT_BY_HEADER_LOCATION is false. + * + * Example: + * // Relative headers + * #include "..." + * + * // Project headers + * #include "..." + * + * // Library headers + * #include <...> + * + * // Forward declarations + * class ...; + */ + public static final String PREF_SEPARATE_INCLUDE_BLOCKS = "separate_include_blocks"; //$NON-NLS-1$ + + /** + * Whether the include directives should be sorted alphabetically. Ignored if + * PREF_ALLOW_TO_REORDER_INCLUDES is false. + */ + public static final String PREF_SORT_ALPHABETICALLY = "sort_alphabetically"; //$NON-NLS-1$ + + /** + * Whether any unused include directives and forward declarations should be removed. It might be + * helpful to disable this if some include directives tend to become removed incorrectly, as it + * might happen when using e.g. conditional compilations. + */ + public static final String PREF_REMOVE_UNUSED_INCLUDES = "remove_unused_includes"; //$NON-NLS-1$ + + public static final String PREF_COMMENT_OUT_UNUSED_INCLUDES = "comment_out_unused_includes"; //$NON-NLS-1$ + + /** + * Initializes the given preference store with the default values. + * + * @param store the preference store to be initialized + */ + public static void initializeDefaultValues(IPreferenceStore store) { + store.setDefault(PREF_FORWARD_DECLARE_COMPOSITE_TYPES, true); + store.setDefault(PREF_FORWARD_DECLARE_ENUMS, false); + store.setDefault(PREF_FORWARD_DECLARE_FUNCTIONS, false); + store.setDefault(PREF_FORWARD_DECLARE_NAMESPACE_ELEMENTS, true); + + // Relative headers preferences + store.setDefault(PREF_RELATIVE_HEADER_IN_SAME_DIR, false); + store.setDefault(PREF_RELATIVE_HEADER_IN_SUB_DIR, false); + store.setDefault(PREF_RELATIVE_HEADER_IN_PARENT_DIR, false); + + // Header resolution preferences + store.setDefault(PREF_HEURISTIC_HEADER_SUBSTITUTION, true); + + // Header sort order preferences + store.setDefault(PREF_ALLOW_TO_REORDER_INCLUDES, true); + store.setDefault(PREF_SORT_BY_HEADER_LOCATION, true); + store.setDefault(PREF_HEADER_LOCATION_SORT_ORDER, + IncludeType.RELATIVE_HEADER.toString() + ' ' + + IncludeType.LIBRARY_HEADER.toString() + ' ' + + IncludeType.PROJECT_HEADER.toString() + ' ' + + IncludeType.FORWARD_DECLARATION.toString() + ' ' + + IncludeType.FUNCTION_FORWARD_DECLARATION.toString() + ' ' + + IncludeType.FOUND_PROBLEM.toString()); + store.setDefault(PREF_SEPARATE_INCLUDE_BLOCKS, true); + store.setDefault(PREF_SORT_ALPHABETICALLY, true); + + // Unused include handling preferences + store.setDefault(PREF_REMOVE_UNUSED_INCLUDES, true); + store.setDefault(PREF_COMMENT_OUT_UNUSED_INCLUDES, true); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java new file mode 100644 index 00000000000..14110018a5a --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2012 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.refactoring.includes; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.content.IContentType; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.index.IIndexFile; +import org.eclipse.cdt.core.index.IIndexFileLocation; +import org.eclipse.cdt.core.index.IndexLocationFactory; + +public class IncludeUtil { + /** Not instantiatable. All methods are static. */ + private IncludeUtil() {} + + /** + * Checks if a file is a source file (.c, .cpp, .cc, etc). Header files are not considered + * source files. + * @return Returns {@code true} if the the file is a source file. + */ + public static boolean isSource(IIndexFile file, IProject project) throws CoreException { + return isSource(getPath(file), project); + } + + /** + * Checks if a file is a source file (.c, .cpp, .cc, etc). Header files are not considered + * source files. + * @return Returns {@code true} if the the file is a source file. + */ + public static boolean isSource(String filename, IProject project) { + IContentType ct= CCorePlugin.getContentType(project, filename); + if (ct != null) { + String id = ct.getId(); + if (CCorePlugin.CONTENT_TYPE_CSOURCE.equals(id) || CCorePlugin.CONTENT_TYPE_CXXSOURCE.equals(id)) { + return true; + } + } + return false; + } + + /** + * Returns the path of the given index file. + * @param file The index file. + * @return The path. + */ + public static String getPath(IIndexFile file) throws CoreException { + return getPath(file.getLocation()); + } + + /** + * Returns the path of the given index file. + * @param fileLocation The index file location. + * @return The path. + */ + public static String getPath(IIndexFileLocation fileLocation) { + return IndexLocationFactory.getAbsolutePath(fileLocation).toOSString(); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java new file mode 100644 index 00000000000..a5a88e92b2a --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * Copyright (c) 2012 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.refactoring.includes; + +import java.io.File; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.parser.IScannerInfo; + +import org.eclipse.cdt.internal.core.parser.scanner.CPreprocessor; +import org.eclipse.cdt.internal.core.parser.scanner.IncludeSearchPath; +import org.eclipse.cdt.internal.core.parser.scanner.IncludeSearchPathElement; +import org.eclipse.cdt.internal.core.parser.scanner.ScannerUtility; + +/** + * Context for managing include statements. + */ +public class InclusionContext { + private static final IPath UNRESOLVED_INCLUDE = new Path(""); //$NON-NLS-1$ + + private final ITranslationUnit fTu; + private final IProject fProject; + private final IPath fCurrentDirectory; + private final IncludePreferences fPreferences; + private final IncludeSearchPath fIncludeSearchPath; + private final Map fIncludeResolutionCache; + private final Map fInverseIncludeResolutionCache; + private final IIndex fIndex; + private final Set fHeadersToInclude; + private final Set fHeadersAlreadyIncluded; + + public InclusionContext(ITranslationUnit tu, IIndex index) { + fTu = tu; + fIndex = index; + ICProject cProject = fTu.getCProject(); + fPreferences = new IncludePreferences(cProject); + fProject = cProject.getProject(); + fCurrentDirectory = fTu.getResource().getParent().getLocation(); + IScannerInfo scannerInfo = fTu.getScannerInfo(true); + fIncludeSearchPath = CPreprocessor.configureIncludeSearchPath(fCurrentDirectory.toFile(), scannerInfo); + fIncludeResolutionCache = new HashMap(); + fInverseIncludeResolutionCache = new HashMap(); + fHeadersToInclude = new HashSet(); + fHeadersAlreadyIncluded = new HashSet(); + } + + public ITranslationUnit getTranslationUnit() { + return fTu; + } + + public IIndex getIndex() { + return fIndex; + } + + public IProject getProject() { + return fProject; + } + + public IncludePreferences getPreferences() { + return fPreferences; + } + + public boolean isCXXLanguage() { + return fTu.isCXXLanguage(); + } + + public IPath getCurrentDirectory() { + return fCurrentDirectory; + } + + public IPath resolveInclude(IncludeInfo include) { + IPath path = fIncludeResolutionCache.get(include); + if (path == null) { + String directory = fCurrentDirectory.toOSString(); + String filePath = CPreprocessor.getAbsoluteInclusionPath(include.getName(), directory); + if (filePath != null) { + path = new Path(filePath); + } else if (!include.isSystem() && !fIncludeSearchPath.isInhibitUseOfCurrentFileDirectory()) { + // Check to see if we find a match in the current directory. + filePath = ScannerUtility.createReconciledPath(directory, include.getName()); + if (fileExists(filePath)) { + path = new Path(filePath); + } + } + + if (path == null) { + for (IncludeSearchPathElement pathElement : fIncludeSearchPath.getElements()) { + if (include.isSystem() && pathElement.isForQuoteIncludesOnly()) + continue; + filePath = pathElement.getLocation(include.getName()); + if (fileExists(filePath)) { + path = new Path(filePath); + break; + } + } + } + if (path == null) + path = UNRESOLVED_INCLUDE; + fIncludeResolutionCache.put(include, path); + fInverseIncludeResolutionCache.put(path, include); + } + return path == UNRESOLVED_INCLUDE ? null : path; + } + + /** + * Returns the include directive that resolves to the given header file, or {@code null} if + * the file is not on the include search path. Current directory is not considered to be a part + * of the include path by this method. + */ + public IncludeInfo getIncludeForHeaderFile(IPath fullPath) { + IncludeInfo include = fInverseIncludeResolutionCache.get(fullPath); + if (include != null) + return include; + String headerLocation = fullPath.toOSString(); + String shortestInclude = null; + boolean isSystem = false; + int count = 0; //XXX + for (IncludeSearchPathElement pathElement : fIncludeSearchPath.getElements()) { + String includeDirective = pathElement.getIncludeDirective(headerLocation); + if (includeDirective != null && + (shortestInclude == null || shortestInclude.length() > includeDirective.length())) { + shortestInclude = includeDirective; + isSystem = !pathElement.isForQuoteIncludesOnly(); + if (count < 1) //XXX + isSystem = false; //XXX Hack to introduce non-system includes + } + count++; //XXX + } + if (shortestInclude == null) + return null; + include = new IncludeInfo(shortestInclude, isSystem); + fIncludeResolutionCache.put(include, fullPath); + fInverseIncludeResolutionCache.put(fullPath, include); + return include; + } + + private static boolean fileExists(String absolutePath) { + return new File(absolutePath).exists(); + } + + public Set getHeadersToInclude() { + return fHeadersToInclude; + } + + public final void addHeaderToInclude(IPath header) { + fHeadersToInclude.add(header); + } + + public final boolean isToBeIncluded(IPath header) { + return fHeadersToInclude.contains(header); + } + + public Set getHeadersAlreadyIncluded() { + return fHeadersAlreadyIncluded; + } + + public final void addHeaderAlreadyIncluded(IPath header) { + fHeadersAlreadyIncluded.add(header); + } + + public final boolean isAlreadyIncluded(IPath header) { + return fHeadersAlreadyIncluded.contains(header); + } + + public final boolean isIncluded(IPath header) { + return fHeadersAlreadyIncluded.contains(header) || fHeadersToInclude.contains(header); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java new file mode 100644 index 00000000000..65202dba345 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2012 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.refactoring.includes; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IPath; + +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.index.IIndexFile; + +class InclusionRequest { + private final IBinding fBinding; + private final Map fDeclaringFiles; + private final boolean fReachable; + private List fCandidatePaths; + private IPath fResolvedPath; + + /** + * @param binding the binding that requires inclusion + * @param declaringHeaders headers that can be included to declare the binding and paths + * that can be used to include them + * @param reachable indicates whether the headers were previously included or not + */ + public InclusionRequest(IBinding binding, Map declaringHeaders, + boolean reachable) { + fBinding = binding; + fDeclaringFiles = Collections.unmodifiableMap(declaringHeaders); + fReachable = reachable; + fCandidatePaths = new ArrayList(new HashSet(fDeclaringFiles.values())); + } + + public IBinding getBinding() { + return fBinding; + } + + public Map getDeclaringFiles() { + return fDeclaringFiles; + } + + public List getCandidatePaths() { + return fCandidatePaths; + } + + public void setCandidatePaths(List paths) { + fCandidatePaths = paths; + } + + public boolean isReachable() { + return fReachable; + } + + public void resolve(IPath path) { + if (fResolvedPath != null) + throw new IllegalStateException(); + fResolvedPath = path; + } + + public IPath getResolvedPath() { + return fResolvedPath; + } + + public boolean isResolved() { + return fResolvedPath != null; + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java new file mode 100644 index 00000000000..310e815cf71 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2012 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.refactoring.includes; + +import org.eclipse.osgi.util.NLS; + +final class Messages extends NLS { + public static String IncludeOrganizer_ChooseHeader; + + static { + NLS.initializeMessages(Messages.class.getName(), Messages.class); + } + + // Do not instantiate + private Messages() { + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties new file mode 100644 index 00000000000..b4b60369ab9 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties @@ -0,0 +1,11 @@ +############################################################################### +# Copyright (c) 2012 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 +############################################################################### +IncludeOrganizer_ChooseHeader=Choose a header file to include for symbol ''{0}'' diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java index 6bcaf7ba388..50c5f56d629 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java @@ -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 @@ -2232,7 +2232,7 @@ public class PreferenceConstants { * the workspace setting should be taken. Note that passing {@code null} should * be avoided. * @param defaultValue The default value if not specified in the preferences. - * @return Returns the current value for the string. + * @return Returns the current value of the preference. * @since 5.1 */ public static int getPreference(String key, ICProject project, int defaultValue) { @@ -2245,7 +2245,7 @@ public class PreferenceConstants { * @param project The current context or {@code null} if no context is available and * the workspace setting should be taken. Note that passing {@code null} should be avoided. * @param defaultValue The default value if not specified in the preferences. - * @return Returns the current value for the string. + * @return Returns the current value of the preference. * @since 5.1 */ public static boolean getPreference(String key, ICProject project, boolean defaultValue) { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java index b7987e54557..366b373cf51 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java @@ -52,6 +52,7 @@ import org.eclipse.cdt.internal.ui.actions.CDTQuickMenuCreator; import org.eclipse.cdt.internal.ui.editor.AddIncludeOnSelectionAction; import org.eclipse.cdt.internal.ui.editor.CEditor; import org.eclipse.cdt.internal.ui.editor.ICEditorActionDefinitionIds; +import org.eclipse.cdt.internal.ui.editor.OrganizeIncludesAction; import org.eclipse.cdt.internal.ui.editor.SortLinesAction; /** @@ -128,7 +129,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange // private ExternalizeStringsAction fExternalizeStrings; // private CleanUpAction fCleanUp; // -// private OrganizeIncludesAction fOrganizeIncludes; + private OrganizeIncludesAction fOrganizeIncludes; // private SortMembersAction fSortMembers; private SortLinesAction fSortLines; private FormatAllAction fFormatAll; @@ -158,10 +159,10 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange fAddInclude.setActionDefinitionId(ICEditorActionDefinitionIds.ADD_INCLUDE); editor.setAction("AddIncludeOnSelection", fAddInclude); //$NON-NLS-1$ -// fOrganizeIncludes= new OrganizeIncludesAction(editor); -// fOrganizeIncludes.setActionDefinitionId(ICEditorActionDefinitionIds.ORGANIZE_INCLUDES); -// editor.setAction("OrganizeIncludes", fOrganizeIncludes); //$NON-NLS-1$ -// + fOrganizeIncludes= new OrganizeIncludesAction(editor); + // TODO: fOrganizeIncludes.setActionDefinitionId(ICEditorActionDefinitionIds.ORGANIZE_INCLUDES); + editor.setAction("OrganizeIncludes", fOrganizeIncludes); //$NON-NLS-1$ + // fSortMembers= new SortMembersAction(editor); // fSortMembers.setActionDefinitionId(ICEditorActionDefinitionIds.SORT_MEMBERS); // editor.setAction("SortMembers", fSortMembers); //$NON-NLS-1$ @@ -431,7 +432,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange added+= addEditorAction(source, "Format"); //$NON-NLS-1$ source.add(new Separator(GROUP_ORGANIZE)); added+= addAction(source, fAddInclude); -// added+= addAction(source, fOrganizeIncludes); + added+= addAction(source, fOrganizeIncludes); // added+= addAction(source, fSortMembers); added+= addAction(source, fSortLines); // added+= addAction(source, fCleanUp); @@ -457,7 +458,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange added+= addAction(source, fFormatAll); source.add(new Separator(GROUP_ORGANIZE)); added+= addAction(source, fAddInclude); -// added+= addAction(source, fOrganizeIncludes); + added+= addAction(source, fOrganizeIncludes); // added+= addAction(source, fSortMembers); // added+= addAction(source, fCleanUp); source.add(new Separator(GROUP_GENERATE)); @@ -506,7 +507,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange // actionBar.setGlobalActionHandler(CdtActionConstants.ADD_CPP_DOC_COMMENT, fAddCppDocStub); // actionBar.setGlobalActionHandler(CdtActionConstants.EXTERNALIZE_STRINGS, fExternalizeStrings); // actionBar.setGlobalActionHandler(CdtActionConstants.CLEAN_UP, fCleanUp); -// actionBar.setGlobalActionHandler(CdtActionConstants.ORGANIZE_INCLUDES, fOrganizeIncludes); +// TODO: actionBar.setGlobalActionHandler(CdtActionConstants.ORGANIZE_INCLUDES, fOrganizeIncludes); // actionBar.setGlobalActionHandler(CdtActionConstants.SORT_MEMBERS, fSortMembers); if (!isEditorOwner()) { // editor provides its own implementation of these actions. From c85a85c81c5bbc807e083047377415b36020967b Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Wed, 9 Jan 2013 17:26:55 -0800 Subject: [PATCH 02/32] Cosmetics --- .../providers/LanguageSettingEntryDialog.java | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingEntryDialog.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingEntryDialog.java index 277cd15bd40..4dec5f8a1ee 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingEntryDialog.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingEntryDialog.java @@ -75,7 +75,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { private static final int COMBO_INDEX_LIBRARY_PATH = 4; private static final int COMBO_INDEX_LIBRARY_FILE = 5; - final private String [] comboKindItems = { + final private String[] comboKindItems = { Messages.LanguageSettingEntryDialog_IncludeDirectory, Messages.LanguageSettingEntryDialog_PreporocessorMacro, Messages.LanguageSettingEntryDialog_IncludeFile, @@ -96,7 +96,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { private static final int COMBO_PATH_INDEX_WORKSPACE = 1; private static final int COMBO_PATH_INDEX_FILESYSTEM = 2; - final private String [] pathCategories = { + final private String[] pathCategories = { Messages.LanguageSettingEntryDialog_ProjectPath, Messages.LanguageSettingEntryDialog_WorkspacePath, Messages.LanguageSettingEntryDialog_Filesystem, @@ -128,7 +128,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { this.cfgDescription = cfgDescription; this.project = cfgDescription.getProjectDescription().getProject(); this.entry = entry; - this.kind = entry!=null ? entry.getKind() : ICSettingEntry.INCLUDE_PATH; + this.kind = entry != null ? entry.getKind() : ICSettingEntry.INCLUDE_PATH; this.clearValue = clearValue; } @@ -165,7 +165,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { } private int kindToComboIndex(int kind) { - int index=0; + int index= 0; switch (kind) { case ICSettingEntry.INCLUDE_PATH: index = COMBO_INDEX_INCLUDE_PATH; @@ -257,7 +257,6 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { pcindex = COMBO_PATH_INDEX_PROJECT; } } - } comboPathCategory.setText(pathCategories[pcindex]); gd = new GridData(SWT.FILL, SWT.NONE, false, false); @@ -357,7 +356,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { // Checkbox "Built-In" checkBoxBuiltIn = new Button(compCheckboxes, SWT.CHECK); checkBoxBuiltIn.setText(Messages.LanguageSettingEntryDialog_BuiltInFlag); - checkBoxBuiltIn.setSelection(entry!=null && (entry.getFlags()&ICSettingEntry.BUILTIN)!=0); + checkBoxBuiltIn.setSelection(entry != null && (entry.getFlags() & ICSettingEntry.BUILTIN) != 0); gd = new GridData(GridData.FILL_HORIZONTAL); checkBoxBuiltIn.setLayoutData(gd); checkBoxBuiltIn.addSelectionListener(new SelectionListener() { @@ -372,10 +371,10 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { } }); - // Checkbox "Framework" + // Checkbox "Framework folder" checkBoxFramework = new Button(compCheckboxes, SWT.CHECK); checkBoxFramework.setText(Messages.LanguageSettingEntryDialog_FrameworkFolder); - checkBoxFramework.setSelection(entry!=null && (entry.getFlags()&ICSettingEntry.FRAMEWORKS_MAC)!=0); + checkBoxFramework.setSelection(entry != null && (entry.getFlags() & ICSettingEntry.FRAMEWORKS_MAC) != 0); gd = new GridData(GridData.FILL_HORIZONTAL); checkBoxFramework.setLayoutData(gd); checkBoxFramework.addSelectionListener(new SelectionListener() { @@ -398,7 +397,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { compButtons.setLayoutData(gd); compButtons.setLayout(new GridLayout(4, false)); - // placeholder + // Placeholder Label placeholder = new Label(compButtons, 0); placeholder.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL)); @@ -445,11 +444,11 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { checkBoxValue.setVisible(isMacroSelected); inputValue.setVisible(isMacroSelected); - ((GridData)checkBoxValue.getLayoutData()).exclude = !isMacroSelected; - ((GridData)inputValue.getLayoutData()).exclude = !isMacroSelected; + ((GridData) checkBoxValue.getLayoutData()).exclude = !isMacroSelected; + ((GridData) inputValue.getLayoutData()).exclude = !isMacroSelected; - ((GridData)buttonBrowse.getLayoutData()).exclude = isMacroSelected; - ((GridData)buttonVars.getLayoutData()).exclude = isMacroSelected; + ((GridData) buttonBrowse.getLayoutData()).exclude = isMacroSelected; + ((GridData) buttonVars.getLayoutData()).exclude = isMacroSelected; switch (kindSelectionIndex) { case COMBO_INDEX_INCLUDE_PATH: @@ -500,7 +499,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { int indexPathKind = comboPathCategory.getSelectionIndex(); int kind = comboKind.getSelectionIndex(); boolean isProjectPath = indexPathKind==COMBO_PATH_INDEX_PROJECT; - boolean isWorkspacePath = (kind!=COMBO_INDEX_MACRO) && (isProjectPath || indexPathKind==COMBO_PATH_INDEX_WORKSPACE); + boolean isWorkspacePath = (kind != COMBO_INDEX_MACRO) && (isProjectPath || indexPathKind == COMBO_PATH_INDEX_WORKSPACE); int flagWorkspace = isWorkspacePath ? ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED : 0; int flags = flagBuiltIn | flagWorkspace | flagFramework; @@ -578,14 +577,15 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { if (str != null) { str = strip_wsp(str); - if (comboPathCategory.getSelectionIndex()==COMBO_PATH_INDEX_PROJECT && str.startsWith(SLASH+project.getName()+SLASH)) { - str=str.substring(project.getName().length()+2); + if (comboPathCategory.getSelectionIndex() == COMBO_PATH_INDEX_PROJECT && str.startsWith(SLASH + project.getName()+SLASH)) { + str = str.substring(project.getName().length() + 2); } inputName.setText(str); } } else if (e.widget.equals(buttonVars)) { str = AbstractCPropertyTab.getVariableDialog(shell, cfgDescription); - if (str != null) inputName.insert(str); + if (str != null) + inputName.insert(str); } } @@ -601,15 +601,14 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { int kind = comboIndexToKind(indexEntryKind); int flagBuiltin = checkBoxBuiltIn.getSelection() ? ICSettingEntry.BUILTIN : 0; int flagFramework = checkBoxFramework.getSelection() ? ICSettingEntry.FRAMEWORKS_MAC : 0; - boolean isWorkspacePath = indexPathKind==COMBO_PATH_INDEX_PROJECT || indexPathKind==COMBO_PATH_INDEX_WORKSPACE; + boolean isWorkspacePath = indexPathKind == COMBO_PATH_INDEX_PROJECT || indexPathKind == COMBO_PATH_INDEX_WORKSPACE; int flagWorkspace = isWorkspacePath ? ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED : 0; int flags = flagBuiltin | flagWorkspace | flagFramework; - Image image = LanguageSettingsImages.getImage(kind, flags, indexPathKind==COMBO_PATH_INDEX_PROJECT); + Image image = LanguageSettingsImages.getImage(kind, flags, indexPathKind == COMBO_PATH_INDEX_PROJECT); iconComboKind.setImage(image); shell.setImage(image); buttonBrowse.setImage(pathCategoryImages[indexPathKind]); } - } From fccf1e95040cb8cd770f9b4039de0a054370a9f5 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Wed, 9 Jan 2013 18:28:55 -0800 Subject: [PATCH 03/32] Externalized a label. --- build/org.eclipse.cdt.managedbuilder.core/plugin.properties | 3 ++- build/org.eclipse.cdt.managedbuilder.core/plugin.xml | 2 +- xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.properties | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build/org.eclipse.cdt.managedbuilder.core/plugin.properties b/build/org.eclipse.cdt.managedbuilder.core/plugin.properties index 46b890e8956..35f4d005b7a 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/plugin.properties +++ b/build/org.eclipse.cdt.managedbuilder.core/plugin.properties @@ -78,5 +78,6 @@ extension-point.name.2 = Build Properties extension-point.name.3 = ToolChain Modification Info GCCBuildOutputParser.name = CDT GCC Build Output Parser -GCCBuildinCompilerSettings.name = CDT GCC Builtin Compiler Settings +GCCBuildinCompilerSettings.name = CDT GCC Built-in Compiler Settings +GCCBuildinCompilerSettingsMinGW.name = CDT GCC Built-in Compiler Settings MinGW ManagedBuildSettingEntries.name = CDT Managed Build Setting Entries \ No newline at end of file diff --git a/build/org.eclipse.cdt.managedbuilder.core/plugin.xml b/build/org.eclipse.cdt.managedbuilder.core/plugin.xml index 427e9b82fd5..f808643a31b 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/plugin.xml +++ b/build/org.eclipse.cdt.managedbuilder.core/plugin.xml @@ -616,7 +616,7 @@ diff --git a/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.properties b/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.properties index 1f8857cdb4d..253ac3cb7cf 100644 --- a/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.properties +++ b/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.properties @@ -18,5 +18,5 @@ objectFileName=Object File profileName=XL C managed make per project scanner discovery profile profileNameCPP=XL C++ managed make per project scanner discovery profile -XlcBuiltinSpecsDetectorName=CDT XLC Builtin Compiler Settings +XlcBuiltinSpecsDetectorName=CDT XLC Built-in Compiler Settings XlcBuildCommandParserName=CDT XLC Build Output Parser From 55e2872e324b35497456d2d8f63703a3c3466750 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Wed, 9 Jan 2013 18:30:27 -0800 Subject: [PATCH 04/32] Bug 388368 - Add Include Directory dialog should distinguish system and non-system includes --- .../providers/LanguageSettingEntryDialog.java | 90 ++++++++++++------- .../cdt/internal/ui/newui/Messages.java | 1 + .../cdt/internal/ui/newui/Messages.properties | 5 +- 3 files changed, 61 insertions(+), 35 deletions(-) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingEntryDialog.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingEntryDialog.java index 4dec5f8a1ee..9fd5e328b10 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingEntryDialog.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingEntryDialog.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010, 2012 Andrew Gvozdev and others. + * Copyright (c) 2010, 2013 Andrew Gvozdev 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 @@ -7,6 +7,7 @@ * * Contributors: * Andrew Gvozdev - initial API and implementation + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.ui.language.settings.providers; @@ -63,16 +64,17 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { private Button buttonBrowse; private Button buttonVars; private Button checkBoxBuiltIn; + private Button checkBoxSystem; private Button checkBoxFramework; private Button buttonOk; private Button buttonCancel; - private static final int COMBO_INDEX_INCLUDE_PATH = 0; + private static final int COMBO_INDEX_INCLUDE_DIR = 0; private static final int COMBO_INDEX_MACRO = 1; private static final int COMBO_INDEX_INCLUDE_FILE = 2; private static final int COMBO_INDEX_MACRO_FILE = 3; - private static final int COMBO_INDEX_LIBRARY_PATH = 4; + private static final int COMBO_INDEX_LIBRARY_DIR = 4; private static final int COMBO_INDEX_LIBRARY_FILE = 5; final private String[] comboKindItems = { @@ -142,7 +144,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { private int comboIndexToKind(int index) { int kind=0; switch (index) { - case COMBO_INDEX_INCLUDE_PATH: + case COMBO_INDEX_INCLUDE_DIR: kind = ICSettingEntry.INCLUDE_PATH; break; case COMBO_INDEX_MACRO: @@ -154,7 +156,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { case COMBO_INDEX_MACRO_FILE: kind = ICSettingEntry.MACRO_FILE; break; - case COMBO_INDEX_LIBRARY_PATH: + case COMBO_INDEX_LIBRARY_DIR: kind = ICSettingEntry.LIBRARY_PATH; break; case COMBO_INDEX_LIBRARY_FILE: @@ -168,7 +170,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { int index= 0; switch (kind) { case ICSettingEntry.INCLUDE_PATH: - index = COMBO_INDEX_INCLUDE_PATH; + index = COMBO_INDEX_INCLUDE_DIR; break; case ICSettingEntry.MACRO: index = COMBO_INDEX_MACRO; @@ -180,7 +182,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { index = COMBO_INDEX_MACRO_FILE; break; case ICSettingEntry.LIBRARY_PATH: - index = COMBO_INDEX_LIBRARY_PATH; + index = COMBO_INDEX_LIBRARY_DIR; break; case ICSettingEntry.LIBRARY_FILE: index = COMBO_INDEX_LIBRARY_FILE; @@ -228,7 +230,6 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { @Override public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); - } }); comboKind.setEnabled(clearValue); @@ -371,6 +372,24 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { } }); + // Checkbox "Contains system includes" + checkBoxSystem = new Button(compCheckboxes, SWT.CHECK); + checkBoxSystem.setText(Messages.LanguageSettingEntryDialog_ContainsSystemHeaders); + checkBoxSystem.setSelection(entry != null && (entry.getFlags() & ICSettingEntry.LOCAL) == 0); + gd = new GridData(GridData.FILL_HORIZONTAL); + checkBoxSystem.setLayoutData(gd); + checkBoxSystem.addSelectionListener(new SelectionListener() { + @Override + public void widgetSelected(SelectionEvent e) { + updateImages(); + setButtons(); + } + @Override + public void widgetDefaultSelected(SelectionEvent e) { + widgetSelected(e); + } + }); + // Checkbox "Framework folder" checkBoxFramework = new Button(compCheckboxes, SWT.CHECK); checkBoxFramework.setText(Messages.LanguageSettingEntryDialog_FrameworkFolder); @@ -437,6 +456,9 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { private void setButtons() { int kindSelectionIndex = comboKind.getSelectionIndex(); + boolean isIncludeDirSelected = (kindSelectionIndex == COMBO_INDEX_INCLUDE_DIR); + checkBoxSystem.setVisible(isIncludeDirSelected); + checkBoxFramework.setVisible(isIncludeDirSelected); boolean isMacroSelected = (kindSelectionIndex == COMBO_INDEX_MACRO); comboPathCategory.setVisible(!isMacroSelected); buttonBrowse.setVisible(!isMacroSelected); @@ -451,8 +473,8 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { ((GridData) buttonVars.getLayoutData()).exclude = isMacroSelected; switch (kindSelectionIndex) { - case COMBO_INDEX_INCLUDE_PATH: - case COMBO_INDEX_LIBRARY_PATH: + case COMBO_INDEX_INCLUDE_DIR: + case COMBO_INDEX_LIBRARY_DIR: labelInput.setText(Messages.LanguageSettingEntryDialog_Path); break; case COMBO_INDEX_INCLUDE_FILE: @@ -472,8 +494,8 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { boolean isWorkspaceSelected = (indexPathKind == COMBO_PATH_INDEX_WORKSPACE); boolean isFilesystemSelected = (indexPathKind == COMBO_PATH_INDEX_FILESYSTEM); - String path = inputName.getText(); - if (path.trim().length() == 0) { + String path = inputName.getText().trim(); + if (path.isEmpty()) { buttonOk.setEnabled(false); } else { buttonOk.setEnabled((isProjectSelected && !path.startsWith(SLASH)) || @@ -489,27 +511,28 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { public void buttonPressed(SelectionEvent e) { String str = null; if (e.widget.equals(buttonOk)) { - String name = inputName.getText(); + String name = inputName.getText().trim(); text1 = name; - String value = inputValue.getText(); + String value = inputValue.getText().trim(); result = true; - int flagBuiltIn = checkBoxBuiltIn.getSelection() ? ICSettingEntry.BUILTIN : 0; - int flagFramework = checkBoxFramework.getSelection() ? ICSettingEntry.FRAMEWORKS_MAC : 0; + int flagBuiltIn = checkBoxBuiltIn.isVisible() && checkBoxBuiltIn.getSelection() ? ICSettingEntry.BUILTIN : 0; + int flagSystem = checkBoxSystem.isVisible() && checkBoxSystem.getSelection() ? 0 : ICSettingEntry.LOCAL; + int flagFramework = checkBoxFramework.isVisible() && checkBoxFramework.getSelection() ? ICSettingEntry.FRAMEWORKS_MAC : 0; int indexPathKind = comboPathCategory.getSelectionIndex(); int kind = comboKind.getSelectionIndex(); - boolean isProjectPath = indexPathKind==COMBO_PATH_INDEX_PROJECT; + boolean isProjectPath = indexPathKind == COMBO_PATH_INDEX_PROJECT; boolean isWorkspacePath = (kind != COMBO_INDEX_MACRO) && (isProjectPath || indexPathKind == COMBO_PATH_INDEX_WORKSPACE); int flagWorkspace = isWorkspacePath ? ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED : 0; - int flags = flagBuiltIn | flagWorkspace | flagFramework; + int flags = flagBuiltIn | flagWorkspace | flagSystem | flagFramework; - ICLanguageSettingEntry entry=null; - switch (comboKind.getSelectionIndex()) { - case COMBO_INDEX_INCLUDE_PATH: + ICLanguageSettingEntry entry = null; + switch (kind) { + case COMBO_INDEX_INCLUDE_DIR: entry = CDataUtil.createCIncludePathEntry(name, flags); break; case COMBO_INDEX_MACRO: - // note that value=null is not supported by CMacroEntry + // Note that value=null is not supported by CMacroEntry entry = CDataUtil.createCMacroEntry(name, value, flags); break; case COMBO_INDEX_INCLUDE_FILE: @@ -518,7 +541,7 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { case COMBO_INDEX_MACRO_FILE: entry = CDataUtil.createCMacroFileEntry(name, flags); break; - case COMBO_INDEX_LIBRARY_PATH: + case COMBO_INDEX_LIBRARY_DIR: entry = CDataUtil.createCLibraryPathEntry(name, flags); break; case COMBO_INDEX_LIBRARY_FILE: @@ -536,8 +559,8 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { boolean isDirectory = false; boolean isFile = false; switch (comboKind.getSelectionIndex()) { - case COMBO_INDEX_INCLUDE_PATH: - case COMBO_INDEX_LIBRARY_PATH: + case COMBO_INDEX_INCLUDE_DIR: + case COMBO_INDEX_LIBRARY_DIR: isDirectory = true; break; case COMBO_INDEX_INCLUDE_FILE: @@ -552,32 +575,32 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { if (isDirectory) { switch (comboPathCategory.getSelectionIndex()) { case COMBO_PATH_INDEX_WORKSPACE: - str = AbstractCPropertyTab.getWorkspaceDirDialog(shell, inputName.getText()); + str = AbstractCPropertyTab.getWorkspaceDirDialog(shell, inputName.getText().trim()); break; case COMBO_PATH_INDEX_PROJECT: - str = AbstractCPropertyTab.getProjectDirDialog(shell, inputName.getText(), project); + str = AbstractCPropertyTab.getProjectDirDialog(shell, inputName.getText().trim(), project); break; case COMBO_PATH_INDEX_FILESYSTEM: - str = AbstractCPropertyTab.getFileSystemDirDialog(shell, inputName.getText()); + str = AbstractCPropertyTab.getFileSystemDirDialog(shell, inputName.getText().trim()); break; } } else if (isFile) { switch (comboPathCategory.getSelectionIndex()) { case COMBO_PATH_INDEX_WORKSPACE: - str = AbstractCPropertyTab.getWorkspaceFileDialog(shell, inputName.getText()); + str = AbstractCPropertyTab.getWorkspaceFileDialog(shell, inputName.getText().trim()); break; case COMBO_PATH_INDEX_PROJECT: - str = AbstractCPropertyTab.getProjectFileDialog(shell, inputName.getText(), project); + str = AbstractCPropertyTab.getProjectFileDialog(shell, inputName.getText().trim(), project); break; case COMBO_PATH_INDEX_FILESYSTEM: - str = AbstractCPropertyTab.getFileSystemFileDialog(shell, inputName.getText()); + str = AbstractCPropertyTab.getFileSystemFileDialog(shell, inputName.getText().trim()); break; } } if (str != null) { str = strip_wsp(str); - if (comboPathCategory.getSelectionIndex() == COMBO_PATH_INDEX_PROJECT && str.startsWith(SLASH + project.getName()+SLASH)) { + if (comboPathCategory.getSelectionIndex() == COMBO_PATH_INDEX_PROJECT && str.startsWith(SLASH + project.getName() + SLASH)) { str = str.substring(project.getName().length() + 2); } inputName.setText(str); @@ -600,10 +623,11 @@ public class LanguageSettingEntryDialog extends AbstractPropertyDialog { int kind = comboIndexToKind(indexEntryKind); int flagBuiltin = checkBoxBuiltIn.getSelection() ? ICSettingEntry.BUILTIN : 0; + int flagSystem = checkBoxSystem.getSelection() ? 0 : ICSettingEntry.LOCAL; int flagFramework = checkBoxFramework.getSelection() ? ICSettingEntry.FRAMEWORKS_MAC : 0; boolean isWorkspacePath = indexPathKind == COMBO_PATH_INDEX_PROJECT || indexPathKind == COMBO_PATH_INDEX_WORKSPACE; int flagWorkspace = isWorkspacePath ? ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED : 0; - int flags = flagBuiltin | flagWorkspace | flagFramework; + int flags = flagBuiltin | flagWorkspace | flagSystem | flagFramework; Image image = LanguageSettingsImages.getImage(kind, flags, indexPathKind == COMBO_PATH_INDEX_PROJECT); iconComboKind.setImage(image); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/newui/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/newui/Messages.java index a8f1063e6d3..29c4a9f4a33 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/newui/Messages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/newui/Messages.java @@ -192,6 +192,7 @@ public class Messages extends NLS { public static String IncludeTab_import; public static String LanguageSettingEntryDialog_Add; public static String LanguageSettingEntryDialog_BuiltInFlag; + public static String LanguageSettingEntryDialog_ContainsSystemHeaders; public static String LanguageSettingEntryDialog_Directory; public static String LanguageSettingEntryDialog_File; public static String LanguageSettingEntryDialog_Filesystem; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/newui/Messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/newui/Messages.properties index 672564a0e8f..4c543c88cd3 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/newui/Messages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/newui/Messages.properties @@ -169,9 +169,10 @@ IncludeDialog_2=Add to all configurations IncludeDialog_3=Add to all languages LanguageSettingEntryDialog_Add=Add LanguageSettingEntryDialog_BuiltInFlag=Treat as built-in -LanguageSettingEntryDialog_Directory=Dir: +LanguageSettingEntryDialog_ContainsSystemHeaders=Contains system headers +LanguageSettingEntryDialog_Directory=Directory: LanguageSettingEntryDialog_File=File: -LanguageSettingEntryDialog_Filesystem=Filesystem +LanguageSettingEntryDialog_Filesystem=File System Path LanguageSettingEntryDialog_FrameworkFolder=Framework folder (Mac only) LanguageSettingEntryDialog_IncludeDirectory=Include Directory LanguageSettingEntryDialog_IncludeFile=Include File From 4de71f65427182bf6ecb49b071f41aa67ff465c7 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Thu, 10 Jan 2013 14:41:10 -0800 Subject: [PATCH 05/32] Cosmetics --- .../ui/preferences/NameStyleBlock.java | 1 - .../utils/ui/controls/TabFolderLayout.java | 28 +++++++++---------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/NameStyleBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/NameStyleBlock.java index 6ef9c311a3a..08e0eee044b 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/NameStyleBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/NameStyleBlock.java @@ -648,7 +648,6 @@ public class NameStyleBlock extends OptionsConfigurationBlock { private class NameStyleAdapter extends ViewerComparator implements ITreeListAdapter, IDialogFieldListener { - @Override public void selectionChanged(TreeListDialogField field) { updateConfigurationBlock(field.getSelectedElements()); diff --git a/core/org.eclipse.cdt.ui/utils.ui/org/eclipse/cdt/utils/ui/controls/TabFolderLayout.java b/core/org.eclipse.cdt.ui/utils.ui/org/eclipse/cdt/utils/ui/controls/TabFolderLayout.java index a9516535121..2cf7e37db34 100644 --- a/core/org.eclipse.cdt.ui/utils.ui/org/eclipse/cdt/utils/ui/controls/TabFolderLayout.java +++ b/core/org.eclipse.cdt.ui/utils.ui/org/eclipse/cdt/utils/ui/controls/TabFolderLayout.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.cdt.utils.ui.controls; - import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; @@ -22,34 +21,33 @@ import org.eclipse.swt.widgets.Layout; * @noextend This class is not intended to be subclassed by clients. */ public class TabFolderLayout extends Layout { - @Override - protected Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache) { + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) return new Point(wHint, hHint); - - Control [] children = composite.getChildren (); + + Control[] children = composite.getChildren(); int count = children.length; int maxWidth = 0, maxHeight = 0; - for (int i=0; i Date: Wed, 23 Jan 2013 19:54:38 -0800 Subject: [PATCH 06/32] Code streamlining. --- .../internal/ui/preferences/NameStyleBlock.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/NameStyleBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/NameStyleBlock.java index 08e0eee044b..d3cd571eee6 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/NameStyleBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/NameStyleBlock.java @@ -39,7 +39,6 @@ import org.eclipse.cdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; import org.eclipse.cdt.internal.ui.util.NameComposer; -import org.eclipse.cdt.internal.ui.viewsupport.ProjectTemplateStore; 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; @@ -254,17 +253,6 @@ public class NameStyleBlock extends OptionsConfigurationBlock { categoryTree.postSetSelection(new StructuredSelection(element)); } - @Override - public boolean hasProjectSpecificOptions(IProject project) { - if (super.hasProjectSpecificOptions(project)) - return true; - - if (project != null) { - return ProjectTemplateStore.hasProjectSpecificTempates(project); - } - return false; - } - @Override protected Control createContents(Composite parent) { pixelConverter = new PixelConverter(parent); @@ -416,11 +404,6 @@ public class NameStyleBlock extends OptionsConfigurationBlock { updateConfigurationBlock(categoryTree.getSelectedElements()); } - @Override - public boolean performOk() { - return super.performOk(); - } - @Override protected void validateSettings(Key changedKey, String oldValue, String newValue) { StatusInfo status = new StatusInfo(); From 64a3b12f941ba9dc884fa830557d829753ea1509 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Thu, 24 Jan 2013 20:33:56 -0800 Subject: [PATCH 07/32] Bug 45203. Beginning of preference UI. --- core/org.eclipse.cdt.ui/plugin.properties | 6 +- core/org.eclipse.cdt.ui/plugin.xml | 11 + .../cdt/internal/ui/ICHelpContextIds.java | 1 + .../preferences/IncludeCategoriesBlock.java | 370 ++++++++++++++++++ .../preferences/IncludeGroupStyleBlock.java | 166 ++++++++ .../ui/preferences/IncludeOrderBlock.java | 71 ++++ .../ui/preferences/IncludeStyleBlock.java | 42 ++ .../IncludeStylePreferencePage.java | 53 +++ .../OptionsConfigurationBlock.java | 4 +- .../ui/preferences/PreferencesMessages.java | 33 +- .../PreferencesMessages.properties | 36 +- .../ui/preferences/TabConfigurationBlock.java | 106 +++++ .../includes/IncludeGroupStyle.java | 165 ++++++++ .../includes/IncludePreferences.java | 12 +- 14 files changed, 1068 insertions(+), 8 deletions(-) create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeCategoriesBlock.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeGroupStyleBlock.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeOrderBlock.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStyleBlock.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStylePreferencePage.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TabConfigurationBlock.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeGroupStyle.java diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties index ec2b1af6ed5..07940aa01da 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,7 @@ CPluginFileTypesPreferencePage.name=File Types CodeStylePreferencePage.name=Code Style codeTemplatePreferencePage.name=Code Templates codeFormatterPreferencePage.name=Formatter +includeStylePreferencePage.name=Includes nameStylePreferencePage.name=Name Style CodeAssistPreferencePage.name=Content Assist CodeAssistAdvancedPreferencePage.name=Advanced @@ -566,7 +567,8 @@ 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.includestyle=include includes style header file system +preferenceKeywords.namestyle=name style file getter setter field variable 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 diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index 727ae91b283..a244e6e268f 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -1223,6 +1223,14 @@ + + + + + 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..b56cc60ed52 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 @@ -87,6 +87,7 @@ 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 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/preferences/IncludeCategoriesBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeCategoriesBlock.java new file mode 100644 index 00000000000..be0c1cd36aa --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeCategoriesBlock.java @@ -0,0 +1,370 @@ +/******************************************************************************* + * 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.List; + +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.refactoring.includes.IncludePreferences; +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 static final Key KEY_STYLE_GROUP_RELATED = getCDTUIKey(IncludePreferences.INCLUDE_STYLE_GROUP_RELATED); + private static final Key KEY_STYLE_PARTNER = getCDTUIKey(IncludePreferences.INCLUDE_STYLE_PARTNER); + private static final Key KEY_STYLE_GROUP_PARTNER = getCDTUIKey(IncludePreferences.INCLUDE_STYLE_GROUP_PARTNER); + private static final Key KEY_STYLE_SAME_FOLDER = getCDTUIKey(IncludePreferences.INCLUDE_STYLE_SAME_FOLDER); + private static final Key KEY_STYLE_GROUP_SAME_FOLDER = getCDTUIKey(IncludePreferences.INCLUDE_STYLE_GROUP_SAME_FOLDER); + private static final Key KEY_STYLE_SUBFOLDER = getCDTUIKey(IncludePreferences.INCLUDE_STYLE_SUBFOLDER); + private static final Key KEY_STYLE_GROUP_SUBFOLDER = getCDTUIKey(IncludePreferences.INCLUDE_STYLE_GROUP_SUBFOLDER); + private static final Key KEY_STYLE_GROUP_SYSTEM = getCDTUIKey(IncludePreferences.INCLUDE_STYLE_GROUP_SYSTEM); + + private static Key[] getAllKeys() { + return new Key[] { + KEY_STYLE_GROUP_RELATED, + KEY_STYLE_PARTNER, + KEY_STYLE_GROUP_PARTNER, + KEY_STYLE_SAME_FOLDER, + KEY_STYLE_GROUP_SAME_FOLDER, + KEY_STYLE_SUBFOLDER, + KEY_STYLE_GROUP_SUBFOLDER, + KEY_STYLE_GROUP_SYSTEM, + }; + } + + private final Category[] rootCategories; + private TreeListDialogField categoryTree; + private PixelConverter pixelConverter; + private StackLayout editorAreaStack; + private Category selectedCategory; + + public IncludeCategoriesBlock(IStatusChangeListener context, IProject project, + IWorkbenchPreferenceContainer container) { + super(context, project, getAllKeys(), container); + rootCategories = createCategories(); + } + + private static Category[] createCategories() { + Category related = new Category(PreferencesMessages.IncludeCategoriesBlock_related_headers_node, + PreferencesMessages.IncludeCategoriesBlock_related_headers_node_description) + .setGroupingKey(KEY_STYLE_GROUP_RELATED); + new Category(PreferencesMessages.IncludeCategoriesBlock_partner_header_node, + PreferencesMessages.IncludeCategoriesBlock_partner_header_node_description, related) + .setIncludeKind(IncludeKind.PARTNER) + .setGroupingKey(KEY_STYLE_GROUP_PARTNER) + .setStyleKey(KEY_STYLE_PARTNER); + new Category(PreferencesMessages.IncludeCategoriesBlock_same_folder_header_node, + PreferencesMessages.IncludeCategoriesBlock_same_folder_header_node_description, related) + .setIncludeKind(IncludeKind.IN_SAME_FOLDER) + .setGroupingKey(KEY_STYLE_GROUP_SAME_FOLDER) + .setStyleKey(KEY_STYLE_SAME_FOLDER); + new Category(PreferencesMessages.IncludeCategoriesBlock_subfolder_header_node, + PreferencesMessages.IncludeCategoriesBlock_subfolder_header_node_description, related) + .setIncludeKind(IncludeKind.IN_SUBFOLDERS) + .setGroupingKey(KEY_STYLE_GROUP_SUBFOLDER) + .setStyleKey(KEY_STYLE_SUBFOLDER); + Category system = new Category(PreferencesMessages.IncludeCategoriesBlock_system_headers_node, + PreferencesMessages.IncludeCategoriesBlock_system_headers_node_description) + .setGroupingKey(KEY_STYLE_GROUP_SYSTEM); + return new Category[] { related, system }; + } + + 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.NameStyleBlock_categories_label); + categoryTree.setViewerComparator(adapter); + + createCategories(); + + for (Category category : rootCategories) { + categoryTree.addElement(category); + } + + Label label = categoryTree.getLabelControl(composite); + GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + gd.verticalAlignment = GridData.BEGINNING; + label.setLayoutData(gd); + + Control tree = categoryTree.getTreeControl(composite); + gd = new GridData(); + gd.horizontalAlignment = GridData.FILL; + gd.grabExcessHorizontalSpace = true; + gd.verticalAlignment = GridData.FILL; + gd.grabExcessVerticalSpace = false; + gd.widthHint = pixelConverter.convertWidthInCharsToPixels(50); + gd.heightHint = pixelConverter.convertHeightInCharsToPixels(12); + tree.setLayoutData(gd); + + createCategoryEditorArea(composite); + + categoryTree.setTreeExpansionLevel(2); + categoryTree.selectFirstElement(); + + updateControls(); + return composite; + } + + private void createCategoryEditorArea(Composite parent) { + Composite editorArea = new Composite(parent, SWT.NONE); + editorArea.setLayoutData(new GridData(GridData.FILL_BOTH)); + editorArea.setFont(parent.getFont()); + editorAreaStack = new StackLayout(); + editorArea.setLayout(editorAreaStack); + for (Category category : rootCategories) { + createCategoryEditor(editorArea, category); + } + } + + private void createCategoryEditor(Composite parent, Category category) { + IncludeGroupStyle style = null; + Key styleKey = category.getStyleKey(); + if (styleKey != null) { + IncludeKind includeKind = category.getIncludeKind(); + String str = getValue(styleKey); + if (str != null) + style = IncludeGroupStyle.fromString(str, includeKind); + if (style == null) + style = new IncludeGroupStyle(includeKind); + } + IncludeGroupStyleBlock block = new IncludeGroupStyleBlock(fContext, fProject, fContainer, + category.getDescription(), category.getGroupingKey(), style); + Control composite = block.createContents(parent); + + category.setEditorArea(composite); + + for (Category child : category.getChildren()) { + createCategoryEditor(parent, child); + } + } + + @Override + protected void updateControls() { + super.updateControls(); + // XXX Implement + } + + private void updateConfigurationBlock(List selection) { + if (selection.size() == 0) + return; + selectedCategory = (Category) selection.get(0); + editorAreaStack.topControl = selectedCategory.getEditorArea(); + editorAreaStack.topControl.getParent().layout(); + } + + @Override + public void performDefaults() { + super.performDefaults(); + + // Refresh + categoryTree.refresh(); + updateConfigurationBlock(categoryTree.getSelectedElements()); + } + + @Override + public boolean performOk() { + return super.performOk(); + } + + @Override + protected void validateSettings(Key changedKey, String oldValue, String newValue) { + StatusInfo status = new StatusInfo(); + fContext.statusChanged(status); + } + + /** + * Represents a category of settings. + */ + private final static class Category { + public final String name; + public final String description; + public final Category parent; + public final int index; // Index in the siblings list + private final List children; + private IncludeKind includeKind; + private Key styleKey; + private Key groupingKey; + + private Control editorArea; + + Category(String name, String description, Category parent) { + this.name = name; + this.description = description; + this.parent = parent; + children = new ArrayList(); + index = parent != null ? parent.addChild(this) : 0; + } + + /** + * @param name Category name + */ + Category(String name, String description) { + this(name, description, null); + } + + 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 name; + } + + IncludeKind getIncludeKind() { + return includeKind; + } + + Category setIncludeKind(IncludeKind includeKind) { + this.includeKind = includeKind; + return this; + } + + Key getStyleKey() { + return styleKey; + } + + Category setStyleKey(Key key) { + this.styleKey = key; + return this; + } + + Key getGroupingKey() { + return groupingKey; + } + + Category setGroupingKey(Key key) { + this.groupingKey = key; + return this; + } + + Control getEditorArea() { + return editorArea; + } + + Category setEditorArea(Control editorArea) { + this.editorArea = editorArea; + return this; + } + + public String getDescription() { + return 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).name; + } + } +} 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..0e0d3c8284d --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeGroupStyleBlock.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * 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.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 final Key groupingKey; + private final IncludeGroupStyle style; + @SuppressWarnings("hiding") + private final ArrayList