diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java index 09254a4bc8c..4ce567f22c6 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 QNX Software Systems and others. + * Copyright (c) 2000, 2015 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 @@ -234,6 +234,22 @@ public class CCorePreferenceConstants { */ public static final String INCLUDE_PRIVATE_PATTERN = "includes.privatePattern"; //$NON-NLS-1$ + /** + * Default value for {@link #INCLUDE_KEEP_PATTERN}. + * @since 5.9 + */ + public static final String DEFAULT_INCLUDE_KEEP_PATTERN = "IWYU\\s+(pragma:?\\s+)?keep"; //$NON-NLS-1$ + + /** + * Preference key for the regular expression pattern that, when appears in a comment on the same + * line as include statement, indicates that the include statement should be preserved when + * organizing includes. + * @see "https://code.google.com/p/include-what-you-use/wiki/IWYUPragmas" + * + * @since 5.9 + */ + public static final String INCLUDE_KEEP_PATTERN = "includes.keepPattern"; //$NON-NLS-1$ + /** * A named preference that controls whether the parser should skip trivial expressions in initializer lists. *

@@ -301,17 +317,31 @@ public class CCorePreferenceConstants { /** * Returns the node in the preference in the given context. + * + * @param key The preference key. + * @param cProject 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. + * @return Returns the node matching the given context. + */ + private static IEclipsePreferences getPreferenceNode(String key, ICProject cProject) { + IProject project = cProject == null ? null : cProject.getProject(); + return getPreferenceNode(key, project); + } + + /** + * Returns the node in the preference in the given context. + * * @param key The preference key. * @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. * @return Returns the node matching the given context. */ - private static IEclipsePreferences getPreferenceNode(String key, ICProject project) { + private static IEclipsePreferences getPreferenceNode(String key, IProject project) { IEclipsePreferences node = null; - if (project != null) { - node = new ProjectScope(project.getProject()).getNode(CCorePlugin.PLUGIN_ID); + node = new ProjectScope(project).getNode(CCorePlugin.PLUGIN_ID); if (node.get(key, null) != null) { return node; } @@ -331,6 +361,7 @@ public class CCorePreferenceConstants { /** * Returns the string value for the given key in the given context. + * * @param key The preference key * @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. @@ -343,6 +374,20 @@ public class CCorePreferenceConstants { /** * Returns the string value for the given key in the given context. + * + * @param key The preference key + * @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. + * @return Returns the current value for the string. + * @since 5.9 + */ + public static String getPreference(String key, IProject project) { + return getPreference(key, project, null); + } + + /** + * Returns the string value for the given key in the given context. + * * @param key The preference key * @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. @@ -354,8 +399,23 @@ public class CCorePreferenceConstants { return getPreferenceNode(key, project).get(key, defaultValue); } + /** + * Returns the string value for the given key in the given context. + * + * @param key The preference key + * @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 of the preference. + * @since 5.9 + */ + public static String getPreference(String key, IProject project, String defaultValue) { + return getPreferenceNode(key, project).get(key, defaultValue); + } + /** * Returns the integer value for the given key in the given context. + * * @param key The preference key * @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. @@ -367,8 +427,23 @@ public class CCorePreferenceConstants { return getPreferenceNode(key, project).getInt(key, defaultValue); } + /** + * Returns the integer value for the given key in the given context. + * + * @param key The preference key + * @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 of the preference. + * @since 5.9 + */ + public static int getPreference(String key, IProject project, int defaultValue) { + return getPreferenceNode(key, project).getInt(key, defaultValue); + } + /** * Returns the boolean value for the given key in the given context. + * * @param key The preference key * @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. @@ -380,6 +455,20 @@ public class CCorePreferenceConstants { return getPreferenceNode(key, project).getBoolean(key, defaultValue); } + /** + * Returns the boolean value for the given key in the given context. + * + * @param key The preference key + * @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 of the preference. + * @since 5.9 + */ + public static boolean getPreference(String key, IProject project, boolean defaultValue) { + return getPreferenceNode(key, project).getBoolean(key, defaultValue); + } + /** * Returns the scopes for preference lookup. * diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java index 4450214ee65..2efc270b1be 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 QNX Software Systems and others. + * Copyright (c) 2000, 2015 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 @@ -68,6 +68,7 @@ public class CCorePreferenceInitializer extends AbstractPreferenceInitializer { defaultPreferences.put(CCorePreferenceConstants.INCLUDE_BEGIN_EXPORTS_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_BEGIN_EXPORTS_PATTERN); defaultPreferences.put(CCorePreferenceConstants.INCLUDE_END_EXPORTS_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_END_EXPORTS_PATTERN); defaultPreferences.put(CCorePreferenceConstants.INCLUDE_PRIVATE_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_PRIVATE_PATTERN); + defaultPreferences.put(CCorePreferenceConstants.INCLUDE_KEEP_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_KEEP_PATTERN); // Scalability defaults. defaultPreferences.putBoolean(CCorePreferenceConstants.SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS, CCorePreferenceConstants.DEFAULT_SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS); diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java index d1840e7e676..936005bbdf2 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2014 Google, Inc and others. + * Copyright (c) 2013, 2015 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 @@ -428,6 +428,23 @@ public class IncludeOrganizerTest extends IncludesTestBase { assertExpectedResults(); } + //h1.h + + //h2.h + + //h3.h + + //source.cpp + //#include "h1.h" + //#include "h2.h" // IWYU pragma: keep + //#include "h3.h" /* IWYU pragma: keep */ + //==================== + //#include "h2.h" // IWYU pragma: keep + //#include "h3.h" /* IWYU pragma: keep */ + public void testPragmaKeep() throws Exception { + assertExpectedResults(); + } + //h1.h //#define M2(t, p) t p diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java index 37ff79e2ad3..71add669ce0 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2014 Google, Inc and others. + * Copyright (c) 2013, 2015 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 @@ -13,12 +13,15 @@ package org.eclipse.cdt.internal.corext.codemanipulation; import java.io.File; import java.util.HashMap; import java.util.Map; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; +import org.eclipse.cdt.core.CCorePreferenceConstants; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.ITranslationUnit; @@ -48,6 +51,7 @@ public class InclusionContext { private final IncludePreferences fPreferences; private String fSourceContents; private String fLineDelimiter; + private Pattern fKeepPragmaPattern; private IPath fTuLocation; public InclusionContext(ITranslationUnit tu) { @@ -295,6 +299,20 @@ public class InclusionContext { return fLineDelimiter; } + public Pattern getKeepPragmaPattern() { + if (fKeepPragmaPattern == null) { + String keepPattern = CCorePreferenceConstants.getPreference( + CCorePreferenceConstants.INCLUDE_KEEP_PATTERN, fProject, + CCorePreferenceConstants.DEFAULT_INCLUDE_KEEP_PATTERN); + try { + fKeepPragmaPattern = Pattern.compile(keepPattern); + } catch (PatternSyntaxException e) { + fKeepPragmaPattern = Pattern.compile(CCorePreferenceConstants.DEFAULT_INCLUDE_KEEP_PATTERN); + } + } + return fKeepPragmaPattern; + } + /** * Sets the effective translation unit location that overrides the default value obtained by * calling {@code getTranslationUnit().getLocation()}. diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludePragmasBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludePragmasBlock.java index 5c5c9493275..b0ad0c16ede 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludePragmasBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludePragmasBlock.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2014 Google, Inc and others. + * Copyright (c) 2013, 2015 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 @@ -45,12 +45,14 @@ public class IncludePragmasBlock extends OptionsConfigurationBlock { private static final Key KEY_BEGIN_EXPORTS_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_BEGIN_EXPORTS_PATTERN); private static final Key KEY_END_EXPORTS_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_END_EXPORTS_PATTERN); private static final Key KEY_PRIVATE_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_PRIVATE_PATTERN); + private static final Key KEY_KEEP_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_KEEP_PATTERN); private static Key[] ALL_KEYS = { KEY_EXPORT_PATTERN, KEY_BEGIN_EXPORTS_PATTERN, KEY_END_EXPORTS_PATTERN, KEY_PRIVATE_PATTERN, + KEY_KEEP_PATTERN, }; private PixelConverter pixelConverter; @@ -83,9 +85,12 @@ public class IncludePragmasBlock extends OptionsConfigurationBlock { control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_end_exports_pattern, KEY_END_EXPORTS_PATTERN, 0, pixelConverter.convertWidthInCharsToPixels(40)); LayoutUtil.setHorizontalGrabbing(control, true); - control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_end_exports_pattern, + control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_private_pattern, KEY_PRIVATE_PATTERN, 0, pixelConverter.convertWidthInCharsToPixels(40)); LayoutUtil.setHorizontalGrabbing(control, true); + control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_keep_pattern, + KEY_KEEP_PATTERN, 0, pixelConverter.convertWidthInCharsToPixels(40)); + LayoutUtil.setHorizontalGrabbing(control, true); updateControls(); return composite; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java index fd430b1f70e..1664bbd466e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2015 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 @@ -469,6 +469,7 @@ public final class PreferencesMessages extends NLS { public static String IncludePragmasBlock_begin_exports_pattern; public static String IncludePragmasBlock_end_exports_pattern; public static String IncludePragmasBlock_private_pattern; + public static String IncludePragmasBlock_keep_pattern; public static String NameStylePreferencePage_title; public static String NameStyleBlock_code_node; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties index 872b44d816a..a9209d344c3 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2014 IBM Corporation and others. +# Copyright (c) 2000, 2015 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 @@ -522,10 +522,11 @@ IncludeOrderBlock_order_of_includes= O&rder of Include Statements: IncludePragmasPreferencePage_title= Include Pragmas IncludePragmasBlock_description=Include pragmas are special comments that affect behavior of Organize Includes command. A description of include pragmas can be found in IWYU Pragmas. Include patterns can be customized by defining regular expressions matching each of the pragmas. IncludePragmasBlock_link_tooltip=Wiki page describing include-what-you-use pragmas -IncludePragmasBlock_export_pattern= &Export: +IncludePragmasBlock_export_pattern= E&xport: IncludePragmasBlock_begin_exports_pattern= &Begin Exports: -IncludePragmasBlock_end_exports_pattern= En&d Exports: +IncludePragmasBlock_end_exports_pattern= &End Exports: IncludePragmasBlock_private_pattern= &Private: +IncludePragmasBlock_keep_pattern= &Keep: NameStylePreferencePage_title=Name Style NameStyleBlock_code_node=Code 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 index d191174daa4..dc5f0e7c679 100644 --- 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2013 Google, Inc and others. + * Copyright (c) 2012, 2015 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 @@ -107,9 +107,9 @@ public class IncludeOrganizer { * {@code null} if the include was not resolved. */ IncludePrototype(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style, - IASTPreprocessorIncludeStatement existingInclude) { + IASTPreprocessorIncludeStatement existingInclude, boolean required) { super(header, includeInfo, style, existingInclude); - this.required = false; + this.required = required; } public boolean isRequired() { @@ -195,6 +195,8 @@ public class IncludeOrganizer { List requests = createInclusionRequests(ast, bindingsToInclude, false, reachableHeaders); processInclusionRequests(requests, headerSubstitutor); + NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast); + // Use a map instead of a set to be able to retrieve existing elements using equal elements. // Maps each element to itself. Map includePrototypes = new HashMap<>(); @@ -215,12 +217,12 @@ public class IncludeOrganizer { IPath header = path.isEmpty() ? null : Path.fromOSString(path); IncludeGroupStyle style = header != null ? fContext.getIncludeStyle(header) : fContext.getIncludeStyle(includeInfo); - IncludePrototype prototype = new IncludePrototype(header, includeInfo, style, include); + boolean required = hasPragmaKeep(include, commentedNodeMap); + IncludePrototype prototype = new IncludePrototype(header, includeInfo, style, include, required); updateIncludePrototypes(includePrototypes, prototype); } } - NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast); IRegion includeReplacementRegion = IncludeUtil.getSafeIncludeReplacementRegion(fContext.getSourceContents(), ast, commentedNodeMap); @@ -934,4 +936,31 @@ public class IncludeOrganizer { buf.append(lineComment); return buf.toString(); } + + private boolean hasPragmaKeep(IASTPreprocessorIncludeStatement include, NodeCommentMap commentedNodeMap) { + List comments = commentedNodeMap.getTrailingCommentsForNode(include); + for (IASTComment comment : comments) { + String text = getTrimmedCommentText(comment); + if (fContext.getKeepPragmaPattern().matcher(text).matches()) + return true; + } + return false; + } + + private String getTrimmedCommentText(IASTComment comment) { + char[] text = comment.getComment(); + int end = text.length - (comment.isBlockComment() ? 2 : 0); + int begin; + for (begin = 2; begin < end; begin++) { + if (!Character.isWhitespace(text[begin])) + break; + } + if (end <= begin) + return ""; //$NON-NLS-1$ + while (--end >= begin) { + if (!Character.isWhitespace(text[end])) + break; + } + return new String(text, begin, end + 1 - begin); + } }