1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-04 23:05:47 +02:00

Bug 414838 - New class creation doesn't properly handle include path

with directories for non-system headers.
This commit is contained in:
Sergey Prigogin 2013-08-11 20:51:32 -07:00
parent 1524122c03
commit eca8e6004c
19 changed files with 497 additions and 418 deletions

View file

@ -36,7 +36,7 @@ import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.ui.testplugin.CTestPlugin; import org.eclipse.cdt.ui.testplugin.CTestPlugin;
import org.eclipse.cdt.internal.ui.refactoring.includes.BindingClassifier; import org.eclipse.cdt.internal.ui.refactoring.includes.BindingClassifier;
import org.eclipse.cdt.internal.ui.refactoring.includes.InclusionContext; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeCreationContext;
/** /**
* Tests for {@link BindingClassifier}. * Tests for {@link BindingClassifier}.
@ -68,7 +68,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase {
if (fBindingClassifier == null) { if (fBindingClassifier == null) {
IASTTranslationUnit ast = getAst(); IASTTranslationUnit ast = getAst();
ITranslationUnit tu = ast.getOriginatingTranslationUnit(); ITranslationUnit tu = ast.getOriginatingTranslationUnit();
InclusionContext context = new InclusionContext(tu, fIndex); IncludeCreationContext context = new IncludeCreationContext(tu, fIndex);
fBindingClassifier = new BindingClassifier(context); fBindingClassifier = new BindingClassifier(context);
fBindingClassifier.classifyNodeContents(ast); fBindingClassifier.classifyNodeContents(ast);
} }

View file

@ -36,7 +36,6 @@ import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.ui.editor.CEditorMessages; import org.eclipse.cdt.internal.ui.editor.CEditorMessages;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeInfo;
/** /**
* Adds includes and 'using' declarations to a translation unit. * Adds includes and 'using' declarations to a translation unit.

View file

@ -8,7 +8,7 @@
* Contributors: * Contributors:
* Sergey Prigogin (Google) - initial API and implementation * Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes; package org.eclipse.cdt.internal.corext.codemanipulation;
import com.ibm.icu.text.Collator; import com.ibm.icu.text.Collator;

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2012, 2013 Google, Inc and others. * Copyright (c) 2013 Google, Inc and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -8,24 +8,17 @@
* Contributors: * Contributors:
* Sergey Prigogin (Google) - initial API and implementation * Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes; package org.eclipse.cdt.internal.corext.codemanipulation;
import java.io.File; import java.io.File;
import java.util.ArrayDeque;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Path;
import org.eclipse.cdt.core.index.IIndex;
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.core.model.ICProject; import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.core.parser.IScannerInfo;
@ -34,65 +27,51 @@ 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.IncludeSearchPath;
import org.eclipse.cdt.internal.core.parser.scanner.IncludeSearchPathElement; import org.eclipse.cdt.internal.core.parser.scanner.IncludeSearchPathElement;
import org.eclipse.cdt.internal.core.parser.scanner.ScannerUtility; import org.eclipse.cdt.internal.core.parser.scanner.ScannerUtility;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
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;
/**
* Context for managing include statements.
*/
public class InclusionContext { public class InclusionContext {
private static final IPath UNRESOLVED_INCLUDE = Path.EMPTY; private static final IPath UNRESOLVED_INCLUDE = Path.EMPTY;
private final ITranslationUnit fTu; private final ITranslationUnit fTu;
private final IProject fProject; private final IProject fProject;
private final IPath fCurrentDirectory; private final IPath fCurrentDirectory;
private final IncludePreferences fPreferences;
private final IncludeSearchPath fIncludeSearchPath; private final IncludeSearchPath fIncludeSearchPath;
private final Map<IncludeInfo, IPath> fIncludeResolutionCache; private final Map<IncludeInfo, IPath> fIncludeResolutionCache;
private final Map<IPath, IncludeInfo> fInverseIncludeResolutionCache; private final Map<IPath, IncludeInfo> fInverseIncludeResolutionCache;
private final IIndex fIndex; private final IncludePreferences fPreferences;
private final Set<IPath> fHeadersToInclude;
private final Set<IPath> fHeadersAlreadyIncluded;
private final Set<IPath> fHeadersIncludedPreviously;
public InclusionContext(ITranslationUnit tu, IIndex index) { public InclusionContext(ITranslationUnit tu) {
fTu = tu; fTu = tu;
fIndex = index;
ICProject cProject = fTu.getCProject(); ICProject cProject = fTu.getCProject();
fPreferences = new IncludePreferences(cProject);
fProject = cProject.getProject(); fProject = cProject.getProject();
fCurrentDirectory = fTu.getResource().getParent().getLocation(); fCurrentDirectory = fTu.getResource().getParent().getLocation();
IScannerInfo scannerInfo = fTu.getScannerInfo(true); IScannerInfo scannerInfo = fTu.getScannerInfo(true);
fIncludeSearchPath = CPreprocessor.configureIncludeSearchPath(fCurrentDirectory.toFile(), scannerInfo); fIncludeSearchPath = CPreprocessor.configureIncludeSearchPath(fCurrentDirectory.toFile(), scannerInfo);
fIncludeResolutionCache = new HashMap<IncludeInfo, IPath>(); fIncludeResolutionCache = new HashMap<IncludeInfo, IPath>();
fInverseIncludeResolutionCache = new HashMap<IPath, IncludeInfo>(); fInverseIncludeResolutionCache = new HashMap<IPath, IncludeInfo>();
fHeadersToInclude = new HashSet<IPath>(); fPreferences = new IncludePreferences(cProject);
fHeadersAlreadyIncluded = new HashSet<IPath>();
fHeadersIncludedPreviously = new HashSet<IPath>();
} }
public ITranslationUnit getTranslationUnit() { public final ITranslationUnit getTranslationUnit() {
return fTu; return fTu;
} }
public IIndex getIndex() { public final IProject getProject() {
return fIndex; return fProject;
} }
public IProject getProject() { public final IPath getCurrentDirectory() {
return fProject; return fCurrentDirectory;
} }
public IncludePreferences getPreferences() { public IncludePreferences getPreferences() {
return fPreferences; return fPreferences;
} }
public boolean isCXXLanguage() {
return fTu.isCXXLanguage();
}
public IPath getCurrentDirectory() {
return fCurrentDirectory;
}
public IPath resolveInclude(IncludeInfo include) { public IPath resolveInclude(IncludeInfo include) {
IPath path = fIncludeResolutionCache.get(include); IPath path = fIncludeResolutionCache.get(include);
if (path == null) { if (path == null) {
@ -161,82 +140,89 @@ public class InclusionContext {
return include; return include;
} }
/** public IncludeGroupStyle getIncludeStyle(IPath headerPath) {
* Removes headers that are exported by other headers that will be included IncludeKind includeKind;
*/ IncludeInfo includeInfo = getIncludeForHeaderFile(headerPath);
public void removeExportedHeaders() throws CoreException { if (includeInfo != null && includeInfo.isSystem()) {
// Index files keyed by their absolute paths. if (headerPath.getFileExtension() == null) {
Map<IPath, IIndexFile> filesByPath = new HashMap<IPath, IIndexFile>(); includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION;
for (IIndexFile file : fIndex.getAllFiles()) { } else {
IPath path = getPath(file); includeKind = IncludeKind.SYSTEM_WITH_EXTENSION;
filesByPath.put(path, file); }
} } else if (isPartnerFile(headerPath)) {
includeKind = IncludeKind.PARTNER;
Set<IPath> exportedHeaders = new HashSet<IPath>(); } else {
for (IPath path : fHeadersToInclude) { IPath dir = getCurrentDirectory();
if (!exportedHeaders.contains(path)) { if (dir.isPrefixOf(headerPath)) {
IIndexFile file = filesByPath.get(path); if (headerPath.segmentCount() == dir.segmentCount() + 1) {
if (file != null) { // file can be null if the header was not indexed. includeKind = IncludeKind.IN_SAME_FOLDER;
ArrayDeque<IIndexFile> queue = new ArrayDeque<IIndexFile>(); } else {
queue.add(file); includeKind = IncludeKind.IN_SUBFOLDER;
while ((file = queue.pollFirst()) != null) { }
for (IIndexInclude include : file.getIncludes()) { } else {
if (fPreferences.allowIndirectInclusion || include.isIncludedFileExported()) { IFile[] files = ResourceLookup.findFilesForLocation(headerPath);
file = fIndex.resolveInclude(include); if (files.length == 0) {
if (file != null) { includeKind = IncludeKind.EXTERNAL;
if (exportedHeaders.add(getPath(file))) } else {
queue.add(file); IProject project = getProject();
} includeKind = IncludeKind.IN_OTHER_PROJECT;
} for (IFile file : files) {
if (file.getProject().equals(project)) {
includeKind = IncludeKind.IN_SAME_PROJECT;
break;
} }
} }
} }
} }
} }
fHeadersToInclude.removeAll(exportedHeaders); return fPreferences.includeStyles.get(includeKind);
}
private static IPath getPath(IIndexFile file) throws CoreException {
return IndexLocationFactory.getAbsolutePath(file.getLocation());
} }
public IncludeGroupStyle getIncludeStyle(IncludeInfo includeInfo) {
IncludeKind includeKind;
IPath path = Path.fromPortableString(includeInfo.getName());
if (includeInfo.isSystem()) {
if (path.getFileExtension() == null) {
includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION;
} else {
includeKind = IncludeKind.SYSTEM_WITH_EXTENSION;
}
} else if (isPartnerFile(path)) {
includeKind = IncludeKind.PARTNER;
} else {
includeKind = IncludeKind.EXTERNAL;
}
return fPreferences.includeStyles.get(includeKind);
}
private static boolean fileExists(String absolutePath) { private static boolean fileExists(String absolutePath) {
return new File(absolutePath).exists(); return new File(absolutePath).exists();
} }
public Set<IPath> getHeadersToInclude() { /**
return fHeadersToInclude; * 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.
*/
public boolean isPartnerFile(IPath path) {
String headerName = path.removeFileExtension().lastSegment();
String sourceName = 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 : fPreferences.partnerFileSuffixes) {
if (suffix.equalsIgnoreCase(s))
return true;
}
}
return false;
} }
}
public final void addHeaderToInclude(IPath header) {
fHeadersToInclude.add(header);
}
public final boolean isToBeIncluded(IPath header) {
return fHeadersToInclude.contains(header);
}
public Set<IPath> 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);
}
public final void addHeaderIncludedPreviously(IPath header) {
fHeadersIncludedPreviously.add(header);
}
public final boolean wasIncludedPreviously(IPath header) {
return fHeadersIncludedPreviously.contains(header);
}
}

View file

@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (c) 2012, 2013 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.corext.codemanipulation;
import org.eclipse.core.runtime.IPath;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle;
/**
* Represents a new or an existing include statement together with the style associated with it.
*/
public class StyledInclude implements Comparable<StyledInclude> {
private final IPath header; // null for existing unresolved includes
private final IncludeInfo includeInfo; // never null
private final IncludeGroupStyle style;
private IASTPreprocessorIncludeStatement existingInclude;
/** Initializes a StyledInclude object for a new include */
public StyledInclude(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style) {
this(header, includeInfo, style, null);
if (header == null)
throw new NullPointerException();
}
/**
* Initializes an include prototype object for an existing include. {@code header} may be
* {@code null} if the include was not resolved.
*/
public StyledInclude(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style,
IASTPreprocessorIncludeStatement existingInclude) {
if (includeInfo == null)
throw new NullPointerException();
this.header = header;
this.includeInfo = includeInfo;
this.style = style;
this.existingInclude = existingInclude;
}
public IPath getHeader() {
return header;
}
public IncludeInfo getIncludeInfo() {
return includeInfo;
}
public IncludeGroupStyle getStyle() {
return style;
}
public IASTPreprocessorIncludeStatement getExistingInclude() {
return existingInclude;
}
public void setExistingInclude(IASTPreprocessorIncludeStatement existingInclude) {
this.existingInclude = existingInclude;
}
@Override
public int compareTo(StyledInclude other) {
return includeInfo.compareTo(other.includeInfo);
}
@Override
public int hashCode() {
if (header != null)
return header.hashCode(); // includeInfo is ignored if header is not null
return includeInfo.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StyledInclude other = (StyledInclude) obj;
if (header != null)
return header.equals(other.header); // includeInfo is ignored if header is not null
if (other.header != null)
return false;
return includeInfo.equals(other.includeInfo);
}
/** For debugging only */
@Override
public String toString() {
return header != null ? header.toPortableString() : includeInfo.toString();
}
}

View file

@ -87,12 +87,12 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.internal.corext.codemanipulation.AddIncludesOperation; import org.eclipse.cdt.internal.corext.codemanipulation.AddIncludesOperation;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
import org.eclipse.cdt.internal.corext.codemanipulation.InclusionContext;
import org.eclipse.cdt.internal.ui.CHelpProviderManager; import org.eclipse.cdt.internal.ui.CHelpProviderManager;
import org.eclipse.cdt.internal.ui.ICHelpContextIds; import org.eclipse.cdt.internal.ui.ICHelpContextIds;
import org.eclipse.cdt.internal.ui.actions.WorkbenchRunnableAdapter; import org.eclipse.cdt.internal.ui.actions.WorkbenchRunnableAdapter;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeInfo;
import org.eclipse.cdt.internal.ui.refactoring.includes.InclusionContext;
import org.eclipse.cdt.internal.ui.util.ExceptionHandler; import org.eclipse.cdt.internal.ui.util.ExceptionHandler;
/** /**
@ -205,7 +205,7 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
*/ */
private void deduceInclude(ITextSelection selection, IIndex index, IASTTranslationUnit ast, String[] lookupName) private void deduceInclude(ITextSelection selection, IIndex index, IASTTranslationUnit ast, String[] lookupName)
throws CoreException { throws CoreException {
fContext = new InclusionContext(fTu, index); fContext = new InclusionContext(fTu);
IASTNodeSelector selector = ast.getNodeSelector(fTu.getLocation().toOSString()); IASTNodeSelector selector = ast.getNodeSelector(fTu.getLocation().toOSString());
IASTName name = selector.findEnclosingName(selection.getOffset(), selection.getLength()); IASTName name = selector.findEnclosingName(selection.getOffset(), selection.getLength());
if (name == null) { if (name == null) {

View file

@ -30,10 +30,12 @@ import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser;
* Dialog-based header chooser. * Dialog-based header chooser.
*/ */
public class InteractiveHeaderChooser implements IHeaderChooser { public class InteractiveHeaderChooser implements IHeaderChooser {
private final String title;
private final Shell shell; private final Shell shell;
Map<Collection<IPath>, IPath> userChoiceCache; private final Map<Collection<IPath>, IPath> userChoiceCache;
public InteractiveHeaderChooser(Shell shell) { public InteractiveHeaderChooser(String title, Shell shell) {
this.title = title;
this.shell = shell; this.shell = shell;
userChoiceCache = new HashMap<Collection<IPath>, IPath>(); userChoiceCache = new HashMap<Collection<IPath>, IPath>();
} }
@ -62,7 +64,7 @@ public class InteractiveHeaderChooser implements IHeaderChooser {
ElementListSelectionDialog dialog = ElementListSelectionDialog dialog =
new ElementListSelectionDialog(shell, new LabelProvider()); new ElementListSelectionDialog(shell, new LabelProvider());
dialog.setElements(elemArray); dialog.setElements(elemArray);
dialog.setTitle(CEditorMessages.OrganizeIncludes_label); dialog.setTitle(title);
dialog.setMessage(NLS.bind(CEditorMessages.OrganizeIncludes_choose_header, bindingName)); dialog.setMessage(NLS.bind(CEditorMessages.OrganizeIncludes_choose_header, bindingName));
if (dialog.open() == Window.OK) { if (dialog.open() == Window.OK) {
selectedElement[0] = (IPath) dialog.getFirstResult(); selectedElement[0] = (IPath) dialog.getFirstResult();

View file

@ -49,7 +49,7 @@ import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer;
public class OrganizeIncludesAction extends TextEditorAction { public class OrganizeIncludesAction extends TextEditorAction {
/** /**
* Constructor * Constructor
* @param editor The editor on which this organize includes action should operate. * @param editor The editor on which this Organize Includes action should operate.
*/ */
public OrganizeIncludesAction(ITextEditor editor) { public OrganizeIncludesAction(ITextEditor editor) {
super(CEditorMessages.getBundleForConstructedKeys(), "OrganizeIncludes.", editor); //$NON-NLS-1$ super(CEditorMessages.getBundleForConstructedKeys(), "OrganizeIncludes.", editor); //$NON-NLS-1$
@ -67,7 +67,8 @@ public class OrganizeIncludesAction extends TextEditorAction {
return; return;
} }
final IHeaderChooser headerChooser = new InteractiveHeaderChooser(editor.getSite().getShell()); final IHeaderChooser headerChooser = new InteractiveHeaderChooser(
CEditorMessages.OrganizeIncludes_label, editor.getSite().getShell());
final String lineDelimiter = getLineDelimiter(editor); final String lineDelimiter = getLineDelimiter(editor);
final List<TextEdit> edits = new ArrayList<TextEdit>(); final List<TextEdit> edits = new ArrayList<TextEdit>();
SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) { SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) {

View file

@ -56,11 +56,12 @@ import org.eclipse.ui.XMLMemento;
import com.ibm.icu.text.Collator; import com.ibm.icu.text.Collator;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
import org.eclipse.cdt.internal.ui.ICHelpContextIds; import org.eclipse.cdt.internal.ui.ICHelpContextIds;
import org.eclipse.cdt.internal.ui.dialogs.ResizableStatusDialog; import org.eclipse.cdt.internal.ui.dialogs.ResizableStatusDialog;
import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.cdt.internal.ui.refactoring.includes.HeaderSubstitutionMap; import org.eclipse.cdt.internal.ui.refactoring.includes.HeaderSubstitutionMap;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeInfo;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeMap; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeMap;
import org.eclipse.cdt.internal.ui.wizards.dialogfields.ComboDialogField; import org.eclipse.cdt.internal.ui.wizards.dialogfields.ComboDialogField;
import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField;

View file

@ -111,7 +111,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
* must be defined and a set of bindings that must be declared. * must be defined and a set of bindings that must be declared.
*/ */
public class BindingClassifier { public class BindingClassifier {
private final InclusionContext fContext; private final IncludeCreationContext fContext;
private final IncludePreferences fPreferences; private final IncludePreferences fPreferences;
/** The bindings which require a full definition. */ /** The bindings which require a full definition. */
private final Set<IBinding> fBindingsToDefine; private final Set<IBinding> fBindingsToDefine;
@ -126,7 +126,7 @@ public class BindingClassifier {
/** /**
* @param context the context for binding classification * @param context the context for binding classification
*/ */
public BindingClassifier(InclusionContext context) { public BindingClassifier(IncludeCreationContext context) {
fContext = context; fContext = context;
fPreferences = context.getPreferences(); fPreferences = context.getPreferences();
fBindingsToDefine = new HashSet<IBinding>(); fBindingsToDefine = new HashSet<IBinding>();

View file

@ -24,6 +24,8 @@ import org.eclipse.ui.XMLMemento;
import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
/** /**
* A set of header file substitution rules. * A set of header file substitution rules.
*/ */

View file

@ -32,13 +32,14 @@ import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.PreferenceConstants; import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
public class HeaderSubstitutor { public class HeaderSubstitutor {
private final InclusionContext fContext; private final IncludeCreationContext fContext;
private IncludeMap[] fIncludeMaps; private IncludeMap[] fIncludeMaps;
private SymbolExportMap fSymbolExportMap; private SymbolExportMap fSymbolExportMap;
public HeaderSubstitutor(InclusionContext context) { public HeaderSubstitutor(IncludeCreationContext context) {
fContext = context; fContext = context;
fIncludeMaps = new IncludeMap[] { new IncludeMap(true), new IncludeMap(false) }; fIncludeMaps = new IncludeMap[] { new IncludeMap(true), new IncludeMap(false) };
IPreferencesService preferences = Platform.getPreferencesService(); IPreferencesService preferences = Platform.getPreferencesService();

View file

@ -0,0 +1,142 @@
/*******************************************************************************
* Copyright (c) 2012, 2013 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.index.IIndex;
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.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.corext.codemanipulation.InclusionContext;
/**
* Context for managing include statements.
*/
public class IncludeCreationContext extends InclusionContext {
private final IIndex fIndex;
private final Set<IPath> fHeadersToInclude;
private final Set<IPath> fHeadersAlreadyIncluded;
private final Set<IPath> fHeadersIncludedPreviously;
public IncludeCreationContext(ITranslationUnit tu, IIndex index) {
super(tu);
fIndex = index;
fHeadersToInclude = new HashSet<IPath>();
fHeadersAlreadyIncluded = new HashSet<IPath>();
fHeadersIncludedPreviously = new HashSet<IPath>();
}
public char[] getSourceContents() {
return getTranslationUnit().getContents();
}
public IIndex getIndex() {
return fIndex;
}
public boolean isCXXLanguage() {
return getTranslationUnit().isCXXLanguage();
}
/**
* Removes headers that are exported by other headers that will be included
*/
public void removeExportedHeaders() throws CoreException {
// Index files keyed by their absolute paths.
Map<IPath, IIndexFile> filesByPath = new HashMap<IPath, IIndexFile>();
for (IIndexFile file : fIndex.getAllFiles()) {
IPath path = getPath(file);
filesByPath.put(path, file);
}
Set<IPath> exportedHeaders = new HashSet<IPath>();
for (IPath path : fHeadersToInclude) {
if (!exportedHeaders.contains(path)) {
IIndexFile file = filesByPath.get(path);
if (file != null) { // file can be null if the header was not indexed.
ArrayDeque<IIndexFile> queue = new ArrayDeque<IIndexFile>();
queue.add(file);
while ((file = queue.pollFirst()) != null) {
for (IIndexInclude include : file.getIncludes()) {
if (getPreferences().allowIndirectInclusion || include.isIncludedFileExported()) {
file = fIndex.resolveInclude(include);
if (file != null) {
if (exportedHeaders.add(getPath(file)))
queue.add(file);
}
}
}
}
}
}
}
fHeadersToInclude.removeAll(exportedHeaders);
}
private static IPath getPath(IIndexFile file) throws CoreException {
return IndexLocationFactory.getAbsolutePath(file.getLocation());
}
public Set<IPath> getHeadersToInclude() {
return fHeadersToInclude;
}
public final void addHeaderToInclude(IPath header) {
fHeadersToInclude.add(header);
}
public final boolean isToBeIncluded(IPath header) {
return fHeadersToInclude.contains(header);
}
public Set<IPath> 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);
}
public void addHeadersIncludedPreviously(IASTPreprocessorIncludeStatement[] includes) {
for (IASTPreprocessorIncludeStatement include : includes) {
if (include.isPartOfTranslationUnitFile()) {
String path = include.getPath();
// An empty path means that the include was not resolved.
if (!path.isEmpty())
fHeadersIncludedPreviously.add(Path.fromOSString(path));
}
}
}
public final boolean wasIncludedPreviously(IPath header) {
return fHeadersIncludedPreviously.contains(header);
}
}

View file

@ -15,6 +15,7 @@ import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.eclipse.ui.IMemento; import org.eclipse.ui.IMemento;
@ -262,4 +263,21 @@ public class IncludeGroupStyle implements Comparable<IncludeGroupStyle> {
return c; return c;
return includeKind.ordinal() - other.includeKind.ordinal(); return includeKind.ordinal() - other.includeKind.ordinal();
} }
public IncludeGroupStyle getGroupingStyle(Map<IncludeKind, IncludeGroupStyle> stylesMap) {
if (keepTogether)
return this;
IncludeGroupStyle parent = getParentStyle(stylesMap);
if (parent != null && (parent.keepTogether || parent.includeKind == IncludeKind.OTHER))
return parent;
return stylesMap.get(IncludeKind.OTHER);
}
public IncludeGroupStyle getParentStyle(Map<IncludeKind, IncludeGroupStyle> stylesMap) {
IncludeKind kind = includeKind.parent;
if (kind == null)
return null;
return stylesMap.get(kind);
}
} }

View file

@ -23,6 +23,8 @@ import java.util.Set;
import org.eclipse.ui.IMemento; import org.eclipse.ui.IMemento;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
/** /**
* A set of header file substitution rules. * A set of header file substitution rules.
*/ */

View file

@ -16,6 +16,7 @@ import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getEnd
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeEndOffset; import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeEndOffset;
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeOffset; import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeOffset;
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getStartingLineNumber; import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getStartingLineNumber;
import static org.eclipse.cdt.internal.ui.refactoring.includes.IncludeUtil.isContainedInRegion;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -26,8 +27,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IPath;
@ -89,9 +88,8 @@ import org.eclipse.cdt.internal.core.parser.scanner.CharArray;
import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver; import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver;
import org.eclipse.cdt.internal.core.parser.scanner.IncludeGuardDetection; import org.eclipse.cdt.internal.core.parser.scanner.IncludeGuardDetection;
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.IncludeKind;
/** /**
* Organizes the include directives and forward declarations of a source or header file. * Organizes the include directives and forward declarations of a source or header file.
@ -99,88 +97,44 @@ import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.Includ
public class IncludeOrganizer { public class IncludeOrganizer {
private static boolean DEBUG_HEADER_SUBSTITUTION = "true".equalsIgnoreCase(Platform.getDebugOption(CUIPlugin.PLUGIN_ID + "/debug/includeOrganizer/headerSubstitution")); //$NON-NLS-1$ //$NON-NLS-2$ private static boolean DEBUG_HEADER_SUBSTITUTION = "true".equalsIgnoreCase(Platform.getDebugOption(CUIPlugin.PLUGIN_ID + "/debug/includeOrganizer/headerSubstitution")); //$NON-NLS-1$ //$NON-NLS-2$
private static class IncludePrototype implements Comparable<IncludePrototype> { private static final Collator COLLATOR = Collator.getInstance();
final IPath header; // null for existing unresolved includes
final IncludeInfo includeInfo; // never null
IASTPreprocessorIncludeStatement existingInclude; // null for newly added includes
final boolean required; // true if the header has to be included
final IncludeGroupStyle style;
/** Initializes an include prototype for a new include */ /**
* Represents a new or an existing include statement.
*/
private static class IncludePrototype extends StyledInclude {
private final boolean required; // true if the header has to be included
/** Initializes an include prototype object for a new include */
IncludePrototype(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style) { IncludePrototype(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style) {
if (includeInfo == null) super(header, includeInfo, style);
throw new NullPointerException();
this.header = header;
this.includeInfo = includeInfo;
this.style = style;
this.required = true; this.required = true;
} }
/** /**
* Initializes an include prototype for an existing include. {@code header} may be * Initializes an include prototype object for an existing include. {@code header} may be
* {@code null} if the include was not resolved. * {@code null} if the include was not resolved.
*/ */
IncludePrototype(IASTPreprocessorIncludeStatement include, IPath header, IncludePrototype(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style,
IncludeInfo includeInfo, IncludeGroupStyle style) { IASTPreprocessorIncludeStatement existingInclude) {
if (includeInfo == null) super(header, includeInfo, style, existingInclude);
throw new NullPointerException();
this.existingInclude = include;
this.header = header;
this.includeInfo = includeInfo;
this.style = style;
this.required = false; this.required = false;
} }
public void updateFrom(IncludePrototype other) { public boolean isRequired() {
this.existingInclude = other.existingInclude; return required;
}
@Override
public int hashCode() {
if (header != null)
return header.hashCode(); // includeInfo is ignored if header is not null
return includeInfo.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IncludePrototype other = (IncludePrototype) obj;
if (header != null)
return header.equals(other.header); // includeInfo is ignored if header is not null
if (other.header != null)
return false;
return includeInfo.equals(other.includeInfo);
}
/** For debugging only */
@Override
public String toString() {
return header != null ? header.toPortableString() : includeInfo.toString();
}
@Override
public int compareTo(IncludePrototype other) {
return includeInfo.compareTo(other.includeInfo);
} }
} }
private static final Collator COLLATOR = Collator.getInstance();
private final IHeaderChooser fHeaderChooser; private final IHeaderChooser fHeaderChooser;
private final InclusionContext fContext; private final IncludeCreationContext fContext;
private final String fLineDelimiter; private final String fLineDelimiter;
public IncludeOrganizer(ITranslationUnit tu, IIndex index, String lineDelimiter, public IncludeOrganizer(ITranslationUnit tu, IIndex index, String lineDelimiter,
IHeaderChooser headerChooser) { IHeaderChooser headerChooser) {
fLineDelimiter = lineDelimiter; fLineDelimiter = lineDelimiter;
fHeaderChooser = headerChooser; fHeaderChooser = headerChooser;
fContext = new InclusionContext(tu, index); fContext = new IncludeCreationContext(tu, index);
} }
/** /**
@ -195,14 +149,7 @@ public class IncludeOrganizer {
Set<IBinding> bindingsToDefine = bindingClassifier.getBindingsToDefine(); Set<IBinding> bindingsToDefine = bindingClassifier.getBindingsToDefine();
IASTPreprocessorIncludeStatement[] existingIncludes = ast.getIncludeDirectives(); IASTPreprocessorIncludeStatement[] existingIncludes = ast.getIncludeDirectives();
for (IASTPreprocessorIncludeStatement include : existingIncludes) { fContext.addHeadersIncludedPreviously(existingIncludes);
if (include.isPartOfTranslationUnitFile()) {
String path = include.getPath();
// An empty path means that the include was not resolved.
if (!path.isEmpty())
fContext.addHeaderIncludedPreviously(Path.fromOSString(path));
}
}
HeaderSubstitutor headerSubstitutor = new HeaderSubstitutor(fContext); HeaderSubstitutor headerSubstitutor = new HeaderSubstitutor(fContext);
// Create the list of header files which have to be included by examining the list of // Create the list of header files which have to be included by examining the list of
@ -218,12 +165,12 @@ public class IncludeOrganizer {
new HashMap<IncludePrototype, IncludePrototype>(); new HashMap<IncludePrototype, IncludePrototype>();
// Put the new includes into includePrototypes. // Put the new includes into includePrototypes.
for (IPath header : fContext.getHeadersToInclude()) { for (IPath header : fContext.getHeadersToInclude()) {
IncludeGroupStyle style = getIncludeStyle(header); IncludeGroupStyle style = fContext.getIncludeStyle(header);
IncludeInfo includeInfo = createIncludeInfo(header, style); IncludeInfo includeInfo = createIncludeInfo(header, style);
IncludePrototype prototype = new IncludePrototype(header, includeInfo, style); IncludePrototype prototype = new IncludePrototype(header, includeInfo, style);
updateIncludePrototypes(includePrototypes, prototype); updateIncludePrototypes(includePrototypes, prototype);
} }
// Put the existing includes into includePrototypes. // Add existing includes to includePrototypes.
for (IASTPreprocessorIncludeStatement include : existingIncludes) { for (IASTPreprocessorIncludeStatement include : existingIncludes) {
if (include.isPartOfTranslationUnitFile()) { if (include.isPartOfTranslationUnitFile()) {
String name = new String(include.getName().getSimpleID()); String name = new String(include.getName().getSimpleID());
@ -232,14 +179,15 @@ public class IncludeOrganizer {
// An empty path means that the include was not resolved. // An empty path means that the include was not resolved.
IPath header = path.isEmpty() ? null : Path.fromOSString(path); IPath header = path.isEmpty() ? null : Path.fromOSString(path);
IncludeGroupStyle style = IncludeGroupStyle style =
header != null ? getIncludeStyle(header) : getIncludeStyle(includeInfo); header != null ? fContext.getIncludeStyle(header) : fContext.getIncludeStyle(includeInfo);
IncludePrototype prototype = new IncludePrototype(include, header, includeInfo, style); IncludePrototype prototype = new IncludePrototype(header, includeInfo, style, include);
updateIncludePrototypes(includePrototypes, prototype); updateIncludePrototypes(includePrototypes, prototype);
} }
} }
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast); NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
IRegion includeReplacementRegion = getSafeIncludeReplacementRegion(ast, commentedNodeMap); IRegion includeReplacementRegion =
getSafeIncludeReplacementRegion(fContext.getSourceContents(), ast, commentedNodeMap);
IncludePreferences preferences = fContext.getPreferences(); IncludePreferences preferences = fContext.getPreferences();
boolean allowReordering = preferences.allowReordering || existingIncludes.length == 0; boolean allowReordering = preferences.allowReordering || existingIncludes.length == 0;
@ -250,9 +198,9 @@ public class IncludeOrganizer {
List<IncludePrototype>[] groupedPrototypes = List<IncludePrototype>[] groupedPrototypes =
(List<IncludePrototype>[]) new List<?>[preferences.includeStyles.size()]; (List<IncludePrototype>[]) new List<?>[preferences.includeStyles.size()];
for (IncludePrototype prototype : includePrototypes.keySet()) { for (IncludePrototype prototype : includePrototypes.keySet()) {
if (prototype.existingInclude == null if (prototype.getExistingInclude() == null
|| (allowReordering && isContainedInRegion(prototype.existingInclude, includeReplacementRegion))) { || (allowReordering && isContainedInRegion(prototype.getExistingInclude(), includeReplacementRegion))) {
IncludeGroupStyle groupingStyle = getGroupingStyle(prototype.style); IncludeGroupStyle groupingStyle = prototype.getStyle().getGroupingStyle(preferences.includeStyles);
// If reordering is not allowed, group everything together. // If reordering is not allowed, group everything together.
int position = allowReordering ? groupingStyle.getOrder() : 0; int position = allowReordering ? groupingStyle.getOrder() : 0;
List<IncludePrototype> prototypes = groupedPrototypes[position]; List<IncludePrototype> prototypes = groupedPrototypes[position];
@ -262,15 +210,15 @@ public class IncludeOrganizer {
} }
prototypes.add(prototype); prototypes.add(prototype);
} }
if (!allowReordering && prototype.existingInclude != null if (!allowReordering && prototype.getExistingInclude() != null
&& !prototype.required && prototype.header != null // Unused and resolved. && !prototype.isRequired() && prototype.getHeader() != null // Unused and resolved.
&& isContainedInRegion(prototype.existingInclude, includeReplacementRegion)) { && isContainedInRegion(prototype.getExistingInclude(), includeReplacementRegion)) {
switch (preferences.unusedStatementsDisposition) { switch (preferences.unusedStatementsDisposition) {
case REMOVE: case REMOVE:
createDelete(prototype.existingInclude, edits); createDelete(prototype.getExistingInclude(), edits);
break; break;
case COMMENT_OUT: case COMMENT_OUT:
createCommentOut(prototype.existingInclude, edits); createCommentOut(prototype.getExistingInclude(), edits);
break; break;
case KEEP: case KEEP:
break; break;
@ -283,9 +231,9 @@ public class IncludeOrganizer {
for (List<IncludePrototype> prototypes : groupedPrototypes) { for (List<IncludePrototype> prototypes : groupedPrototypes) {
if (prototypes != null && !prototypes.isEmpty()) { if (prototypes != null && !prototypes.isEmpty()) {
Collections.sort(prototypes); Collections.sort(prototypes);
IncludeGroupStyle style = prototypes.get(0).style; IncludeGroupStyle style = prototypes.get(0).getStyle();
IncludeGroupStyle groupingStyle = getGroupingStyle(style); IncludeGroupStyle groupingStyle = style.getGroupingStyle(preferences.includeStyles);
IncludeGroupStyle parentStyle = getParentStyle(groupingStyle); IncludeGroupStyle parentStyle = groupingStyle.getParentStyle(preferences.includeStyles);
boolean blankLineBefore = groupingStyle.isBlankLineBefore() || boolean blankLineBefore = groupingStyle.isBlankLineBefore() ||
(parentStyle != null && parentStyle != previousParentStyle && (parentStyle != null && parentStyle != previousParentStyle &&
parentStyle.isKeepTogether() && parentStyle.isBlankLineBefore()); parentStyle.isKeepTogether() && parentStyle.isBlankLineBefore());
@ -294,9 +242,9 @@ public class IncludeOrganizer {
includeDirectives.add(""); // Blank line separator //$NON-NLS-1$ includeDirectives.add(""); // Blank line separator //$NON-NLS-1$
for (IncludePrototype prototype : prototypes) { for (IncludePrototype prototype : prototypes) {
String trailingComment = ""; //$NON-NLS-1$ String trailingComment = ""; //$NON-NLS-1$
IASTPreprocessorIncludeStatement include = prototype.existingInclude; IASTPreprocessorIncludeStatement include = prototype.getExistingInclude();
if (include == null if (include == null
|| (allowReordering && isContainedInRegion(include, includeReplacementRegion))) { || (allowReordering && IncludeUtil.isContainedInRegion(include, includeReplacementRegion))) {
if (include != null) { if (include != null) {
List<IASTComment> comments = commentedNodeMap.getTrailingCommentsForNode(include); List<IASTComment> comments = commentedNodeMap.getTrailingCommentsForNode(include);
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
@ -522,7 +470,7 @@ public class IncludeOrganizer {
IASTFileLocation location = include.getFileLocation(); IASTFileLocation location = include.getFileLocation();
int offset = location.getNodeOffset(); int offset = location.getNodeOffset();
if (fContext.getTranslationUnit().isCXXLanguage()) { if (fContext.getTranslationUnit().isCXXLanguage()) {
offset = getLineStart(offset); offset = getLineStart(fContext.getSourceContents(), offset);
edits.add(new InsertEdit(offset, "//")); //$NON-NLS-1$ edits.add(new InsertEdit(offset, "//")); //$NON-NLS-1$
} else { } else {
edits.add(new InsertEdit(offset, "/*")); //$NON-NLS-1$ edits.add(new InsertEdit(offset, "/*")); //$NON-NLS-1$
@ -535,8 +483,8 @@ public class IncludeOrganizer {
IASTFileLocation location = include.getFileLocation(); IASTFileLocation location = include.getFileLocation();
int offset = location.getNodeOffset(); int offset = location.getNodeOffset();
int endOffset = offset + location.getNodeLength(); int endOffset = offset + location.getNodeLength();
offset = getLineStart(offset); offset = getLineStart(fContext.getSourceContents(), offset);
endOffset = skipToNextLine(endOffset); endOffset = skipToNextLine(fContext.getSourceContents(), endOffset);
edits.add(new DeleteEdit(offset, endOffset - offset)); edits.add(new DeleteEdit(offset, endOffset - offset));
} }
@ -546,16 +494,12 @@ public class IncludeOrganizer {
if (existing == null) { if (existing == null) {
includePrototypes.put(prototype, prototype); includePrototypes.put(prototype, prototype);
} else { } else {
existing.updateFrom(prototype); existing.setExistingInclude(prototype.getExistingInclude());
} }
} }
private boolean isContainedInRegion(IASTNode node, IRegion region) { static IRegion getSafeIncludeReplacementRegion(char[] contents, IASTTranslationUnit ast,
return getNodeOffset(node) >= region.getOffset() NodeCommentMap commentMap) {
&& getNodeEndOffset(node) <= region.getOffset() + region.getLength();
}
private IRegion getSafeIncludeReplacementRegion(IASTTranslationUnit ast, NodeCommentMap commentMap) {
int maxSafeOffset = ast.getFileLocation().getNodeLength(); int maxSafeOffset = ast.getFileLocation().getNodeLength();
IASTDeclaration[] declarations = ast.getDeclarations(true); IASTDeclaration[] declarations = ast.getDeclarations(true);
if (declarations.length != 0) if (declarations.length != 0)
@ -592,22 +536,22 @@ public class IncludeOrganizer {
} }
if (includeOffset < 0) { if (includeOffset < 0) {
if (includeGuardEndOffset >= 0) { if (includeGuardEndOffset >= 0) {
includeOffset = skipToNextLine(includeGuardEndOffset); includeOffset = skipToNextLine(contents, includeGuardEndOffset);
} else { } else {
includeOffset = 0; includeOffset = 0;
} }
if (!topCommentSkipped) { if (!topCommentSkipped) {
// Skip the first comment block near the top of the file. // Skip the first comment block near the top of the file.
includeOffset = skipStandaloneCommentBlock(includeOffset, maxSafeOffset, ast.getComments(), commentMap); includeOffset = skipStandaloneCommentBlock(contents, includeOffset, maxSafeOffset, ast.getComments(), commentMap);
} }
includeEndOffset = includeOffset; includeEndOffset = includeOffset;
} else { } else {
includeEndOffset = skipToNextLine(includeEndOffset); includeEndOffset = skipToNextLine(contents, includeEndOffset);
} }
return new Region(includeOffset, includeEndOffset - includeOffset); return new Region(includeOffset, includeEndOffset - includeOffset);
} }
private int getNumberOfIncludeGuardStatementsToSkip(IASTTranslationUnit ast) { private static int getNumberOfIncludeGuardStatementsToSkip(IASTTranslationUnit ast) {
IASTPreprocessorStatement statement = findFirstPreprocessorStatement(ast); IASTPreprocessorStatement statement = findFirstPreprocessorStatement(ast);
if (statement == null) if (statement == null)
return 0; return 0;
@ -620,7 +564,7 @@ public class IncludeOrganizer {
} }
char[] contents = ast.getRawSignature().toCharArray(); char[] contents = ast.getRawSignature().toCharArray();
if (offset != 0) if (offset != 0)
Arrays.copyOfRange(contents, offset, contents.length); contents = Arrays.copyOfRange(contents, offset, contents.length);
CharArrayIntMap ppKeywords= new CharArrayIntMap(40, -1); CharArrayIntMap ppKeywords= new CharArrayIntMap(40, -1);
Keywords.addKeywordsPreprocessor(ppKeywords); Keywords.addKeywordsPreprocessor(ppKeywords);
if (IncludeGuardDetection.detectIncludeGuard(new CharArray(contents), new LexerOptions(), ppKeywords) != null) { if (IncludeGuardDetection.detectIncludeGuard(new CharArray(contents), new LexerOptions(), ppKeywords) != null) {
@ -629,39 +573,38 @@ public class IncludeOrganizer {
return num; return num;
} }
private IASTPreprocessorStatement findFirstPreprocessorStatement(IASTTranslationUnit ast) { private static IASTPreprocessorStatement findFirstPreprocessorStatement(IASTTranslationUnit ast) {
for (IASTPreprocessorStatement statement : ast.getAllPreprocessorStatements()) { for (IASTPreprocessorStatement statement : ast.getAllPreprocessorStatements()) {
if (statement.isPartOfTranslationUnitFile()) if (statement.isPartOfTranslationUnitFile())
return statement; return statement;
} }
return null; return null;
} }
private boolean isPragmaOnce(IASTPreprocessorStatement statement) {
private static boolean isPragmaOnce(IASTPreprocessorStatement statement) {
if (!(statement instanceof IASTPreprocessorPragmaStatement)) if (!(statement instanceof IASTPreprocessorPragmaStatement))
return false; return false;
return CharArrayUtils.equals(((IASTPreprocessorPragmaStatement) statement).getMessage(), "once"); //$NON-NLS-1$ return CharArrayUtils.equals(((IASTPreprocessorPragmaStatement) statement).getMessage(), "once"); //$NON-NLS-1$
} }
private int skipToNextLine(int offset) { private static int skipToNextLine(char[] text, int offset) {
char[] contents = fContext.getTranslationUnit().getContents(); while (offset < text.length) {
while (offset < contents.length) { if (text[offset++] == '\n')
if (contents[offset++] == '\n')
break; break;
} }
return offset; return offset;
} }
private int getLineStart(int offset) { private static int getLineStart(char[] text, int offset) {
char[] contents = fContext.getTranslationUnit().getContents();
while (--offset >= 0) { while (--offset >= 0) {
if (contents[offset] == '\n') if (text[offset] == '\n')
break; break;
} }
return offset + 1; return offset + 1;
} }
private int skipToNextLineAfterNode(IASTNode node) { private static int skipToNextLineAfterNode(char[] text, IASTNode node) {
return skipToNextLine(getNodeEndOffset(node)); return skipToNextLine(text, getNodeEndOffset(node));
} }
/** /**
@ -720,7 +663,8 @@ public class IncludeOrganizer {
return ""; //$NON-NLS-1$ return ""; //$NON-NLS-1$
} }
private int skipStandaloneCommentBlock(int offset, int endOffset, IASTComment[] comments, NodeCommentMap commentMap) { private static int skipStandaloneCommentBlock(char[] contents, int offset, int endOffset,
IASTComment[] comments, NodeCommentMap commentMap) {
Map<IASTComment, IASTNode> inverseLeadingMap = new HashMap<IASTComment, IASTNode>(); Map<IASTComment, IASTNode> inverseLeadingMap = new HashMap<IASTComment, IASTNode>();
for (Map.Entry<IASTNode, List<IASTComment>> entry : commentMap.getLeadingMap().entrySet()) { for (Map.Entry<IASTNode, List<IASTComment>> entry : commentMap.getLeadingMap().entrySet()) {
IASTNode node = entry.getKey(); IASTNode node = entry.getKey();
@ -753,11 +697,11 @@ public class IncludeOrganizer {
for (int j = 1; j < leadingComments.size(); j++) { for (int j = 1; j < leadingComments.size(); j++) {
comment = leadingComments.get(j); comment = leadingComments.get(j);
if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1) if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1)
return skipToNextLineAfterNode(previous); return skipToNextLineAfterNode(contents, previous);
previous = comment; previous = comment;
} }
if (getStartingLineNumber(node) > getEndingLineNumber(previous) + 1) if (getStartingLineNumber(node) > getEndingLineNumber(previous) + 1)
return skipToNextLineAfterNode(previous); return skipToNextLineAfterNode(contents, previous);
} }
node = inverseFreestandingMap.get(comment); node = inverseFreestandingMap.get(comment);
if (node != null) { if (node != null) {
@ -766,7 +710,7 @@ public class IncludeOrganizer {
for (int j = 1; j < freestandingComments.size(); j++) { for (int j = 1; j < freestandingComments.size(); j++) {
comment = freestandingComments.get(j); comment = freestandingComments.get(j);
if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1) if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1)
return skipToNextLineAfterNode(previous); return skipToNextLineAfterNode(contents, previous);
previous = comment; previous = comment;
} }
} }
@ -775,77 +719,6 @@ public class IncludeOrganizer {
return offset; return offset;
} }
private IncludeGroupStyle getGroupingStyle(IncludeGroupStyle style) {
if (style.isKeepTogether())
return style;
IncludeGroupStyle parent = getParentStyle(style);
if (parent != null && (parent.isKeepTogether() || parent.getIncludeKind() == IncludeKind.OTHER))
return parent;
return fContext.getPreferences().includeStyles.get(IncludeKind.OTHER);
}
private IncludeGroupStyle getParentStyle(IncludeGroupStyle style) {
IncludeKind kind = style.getIncludeKind().parent;
if (kind == null)
return null;
return fContext.getPreferences().includeStyles.get(kind);
}
private IncludeGroupStyle getIncludeStyle(IPath headerPath) {
IncludeKind includeKind;
IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(headerPath);
if (includeInfo != null && includeInfo.isSystem()) {
if (headerPath.getFileExtension() == null) {
includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION;
} else {
includeKind = IncludeKind.SYSTEM_WITH_EXTENSION;
}
} else if (isPartnerFile(headerPath)) {
includeKind = IncludeKind.PARTNER;
} else {
IPath dir = fContext.getCurrentDirectory();
if (dir.isPrefixOf(headerPath)) {
if (headerPath.segmentCount() == dir.segmentCount() + 1) {
includeKind = IncludeKind.IN_SAME_FOLDER;
} else {
includeKind = IncludeKind.IN_SUBFOLDER;
}
} else {
IFile[] files = ResourceLookup.findFilesForLocation(headerPath);
if (files.length == 0) {
includeKind = IncludeKind.EXTERNAL;
} else {
IProject project = fContext.getProject();
includeKind = IncludeKind.IN_OTHER_PROJECT;
for (IFile file : files) {
if (file.getProject().equals(project)) {
includeKind = IncludeKind.IN_SAME_PROJECT;
break;
}
}
}
}
}
return fContext.getPreferences().includeStyles.get(includeKind);
}
private IncludeGroupStyle getIncludeStyle(IncludeInfo includeInfo) {
IncludeKind includeKind;
IPath path = Path.fromPortableString(includeInfo.getName());
if (includeInfo.isSystem()) {
if (path.getFileExtension() == null) {
includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION;
} else {
includeKind = IncludeKind.SYSTEM_WITH_EXTENSION;
}
} else if (isPartnerFile(path)) {
includeKind = IncludeKind.PARTNER;
} else {
includeKind = IncludeKind.EXTERNAL;
}
return fContext.getPreferences().includeStyles.get(includeKind);
}
private Set<IBinding> removeBindingsDefinedInIncludedHeaders(IASTTranslationUnit ast, private Set<IBinding> removeBindingsDefinedInIncludedHeaders(IASTTranslationUnit ast,
Set<IBinding> bindings, IIndexFileSet reachableHeaders) throws CoreException { Set<IBinding> bindings, IIndexFileSet reachableHeaders) throws CoreException {
Set<IBinding> filteredBindings = new HashSet<IBinding>(bindings); Set<IBinding> filteredBindings = new HashSet<IBinding>(bindings);
@ -888,7 +761,7 @@ public class IncludeOrganizer {
List<IPath> candidatePaths = request.getCandidatePaths(); List<IPath> candidatePaths = request.getCandidatePaths();
if (candidatePaths.size() == 1) { if (candidatePaths.size() == 1) {
IPath path = candidatePaths.iterator().next(); IPath path = candidatePaths.iterator().next();
if (isPartnerFile(path)) { if (fContext.isPartnerFile(path)) {
request.resolve(path); request.resolve(path);
fContext.addHeaderToInclude(path); fContext.addHeaderToInclude(path);
if (includedByPartner != null) { if (includedByPartner != null) {
@ -1080,33 +953,6 @@ public class IncludeOrganizer {
return headerSubstitutor.getExportingHeaders(symbol); return headerSubstitutor.getExportingHeaders(symbol);
} }
/**
* 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 : fContext.getPreferences().partnerFileSuffixes) {
if (suffix.equalsIgnoreCase(s))
return true;
}
}
return false;
}
private List<InclusionRequest> createInclusionRequests(IASTTranslationUnit ast, private List<InclusionRequest> createInclusionRequests(IASTTranslationUnit ast,
Set<IBinding> bindingsToDefine, boolean allowDeclarations, Set<IBinding> bindingsToDefine, boolean allowDeclarations,
IIndexFileSet reachableHeaders) throws CoreException { IIndexFileSet reachableHeaders) throws CoreException {
@ -1202,7 +1048,7 @@ public class IncludeOrganizer {
private String createIncludeDirective(IncludePrototype include, String lineComment) { private String createIncludeDirective(IncludePrototype include, String lineComment) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
// Unresolved includes are preserved out of caution. // Unresolved includes are preserved out of caution.
if (!include.required && include.header != null) { if (!include.isRequired() && include.getHeader() != null) {
switch (fContext.getPreferences().unusedStatementsDisposition) { switch (fContext.getPreferences().unusedStatementsDisposition) {
case REMOVE: case REMOVE:
return null; return null;
@ -1214,7 +1060,7 @@ public class IncludeOrganizer {
} }
} }
buf.append("#include "); //$NON-NLS-1$ buf.append("#include "); //$NON-NLS-1$
buf.append(include.includeInfo.toString()); buf.append(include.getIncludeInfo().toString());
buf.append(lineComment); buf.append(lineComment);
return buf.toString(); return buf.toString();
} }

View file

@ -10,11 +10,16 @@
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes; package org.eclipse.cdt.internal.ui.refactoring.includes;
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeEndOffset;
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeOffset;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.text.IRegion;
import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.index.IIndexFile; import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexFileLocation; import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.index.IndexLocationFactory; import org.eclipse.cdt.core.index.IndexLocationFactory;
@ -65,4 +70,9 @@ public class IncludeUtil {
public static String getPath(IIndexFileLocation fileLocation) { public static String getPath(IIndexFileLocation fileLocation) {
return IndexLocationFactory.getAbsolutePath(fileLocation).toOSString(); return IndexLocationFactory.getAbsolutePath(fileLocation).toOSString();
} }
public static boolean isContainedInRegion(IASTNode node, IRegion region) {
return getNodeOffset(node) >= region.getOffset()
&& getNodeEndOffset(node) <= region.getOffset() + region.getLength();
}
} }

View file

@ -28,6 +28,8 @@ import org.eclipse.ui.XMLMemento;
import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
/** /**
* A set of header file substitution rules. * A set of header file substitution rules.
*/ */

View file

@ -15,6 +15,7 @@
package org.eclipse.cdt.internal.ui.wizards.classwizard; package org.eclipse.cdt.internal.ui.wizards.classwizard;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -63,13 +64,18 @@ import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.CodeGeneration; import org.eclipse.cdt.ui.CodeGeneration;
import org.eclipse.cdt.utils.PathUtil; import org.eclipse.cdt.utils.PathUtil;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
import org.eclipse.cdt.internal.corext.codemanipulation.InclusionContext;
import org.eclipse.cdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.cdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
import org.eclipse.cdt.internal.corext.util.CModelUtil; import org.eclipse.cdt.internal.corext.util.CModelUtil;
import org.eclipse.cdt.internal.corext.util.CodeFormatterUtil; import org.eclipse.cdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.cdt.internal.corext.util.Strings; import org.eclipse.cdt.internal.corext.util.Strings;
import org.eclipse.cdt.internal.formatter.scanner.Scanner; import org.eclipse.cdt.internal.formatter.scanner.Scanner;
import org.eclipse.cdt.internal.formatter.scanner.Token; import org.eclipse.cdt.internal.formatter.scanner.Token;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences;
import org.eclipse.cdt.internal.ui.wizards.filewizard.NewSourceFileGenerator; import org.eclipse.cdt.internal.ui.wizards.filewizard.NewSourceFileGenerator;
public class NewClassCodeGenerator { public class NewClassCodeGenerator {
@ -703,7 +709,6 @@ public class NewClassCodeGenerator {
ICProject cProject = headerTU.getCProject(); ICProject cProject = headerTU.getCProject();
IProject project = cProject.getProject(); IProject project = cProject.getProject();
IPath projectLocation = new Path(project.getLocationURI().getPath()); IPath projectLocation = new Path(project.getLocationURI().getPath());
IPath headerLocation = new Path(headerTU.getResource().getLocationURI().getPath());
List<IPath> includePaths = getIncludePaths(headerTU); List<IPath> includePaths = getIncludePaths(headerTU);
List<IPath> baseClassPaths = getBaseClassPaths(verifyBaseClasses()); List<IPath> baseClassPaths = getBaseClassPaths(verifyBaseClasses());
@ -716,52 +721,36 @@ public class NewClassCodeGenerator {
} }
} }
List<IPath> systemIncludes = new ArrayList<IPath>(); InclusionContext inclusionContext = new InclusionContext(headerTU);
List<IPath> localIncludes = new ArrayList<IPath>(); List<StyledInclude> includes = new ArrayList<StyledInclude>();
// Sort the include paths into system and local
for (IPath baseClassLocation : baseClassPaths) { for (IPath baseClassLocation : baseClassPaths) {
boolean isSystemIncludePath = false; IncludeInfo includeInfo = inclusionContext.getIncludeForHeaderFile(baseClassLocation);
if (includeInfo != null) {
IncludeGroupStyle style = inclusionContext.getIncludeStyle(includeInfo);
includes.add(new StyledInclude(baseClassLocation, includeInfo, style));
}
}
Collections.sort(includes);
IPath includePath = PathUtil.makeRelativePathToProjectIncludes(baseClassLocation, project);
if (includePath != null && !projectLocation.isPrefixOf(baseClassLocation)) {
isSystemIncludePath = true;
} else if (projectLocation.isPrefixOf(baseClassLocation)
&& projectLocation.isPrefixOf(headerLocation)) {
includePath = PathUtil.makeRelativePath(baseClassLocation, headerLocation.removeLastSegments(1));
}
if (includePath == null)
includePath = baseClassLocation;
// Make the new #include path in the source file only point to a relative file
// (i.e. now that the path has been included above in the project)
includePath = includePath.removeFirstSegments(includePath.segmentCount() - 1).setDevice(null);
if (isSystemIncludePath)
systemIncludes.add(includePath);
else
localIncludes.add(includePath);
}
StringBuilder text = new StringBuilder(); StringBuilder text = new StringBuilder();
// Write the system include paths, e.g. #include <header.h> IncludePreferences preferences = inclusionContext.getPreferences();
for (IPath includePath : systemIncludes) { IncludeGroupStyle previousParentStyle = null;
if (!(headerTU.getElementName().equals(includePath.toString()))) { for (StyledInclude include : includes) {
String include = getIncludeString(includePath.toString(), true); IncludeGroupStyle style = include.getStyle();
text.append(include); IncludeGroupStyle groupingStyle = style.getGroupingStyle(preferences.includeStyles);
text.append(lineDelimiter); IncludeGroupStyle parentStyle = groupingStyle.getParentStyle(preferences.includeStyles);
if (groupingStyle.isBlankLineBefore() ||
(parentStyle != null && previousParentStyle != null &&
parentStyle != previousParentStyle && parentStyle.isKeepTogether() &&
parentStyle.isBlankLineBefore())) {
text.append(lineDelimiter);
} }
} text.append("#include "); //$NON-NLS-1$
text.append(include.getIncludeInfo().toString());
// Write the local include paths, e.g. #include "header.h" text.append(lineDelimiter);
for (IPath includePath : localIncludes) { previousParentStyle = parentStyle;
if (!(headerTU.getElementName().equals(includePath.toString()))) { }
String include = getIncludeString(includePath.toString(), false);
text.append(include);
text.append(lineDelimiter);
}
}
monitor.done(); monitor.done();
return text.toString(); return text.toString();
} }
@ -1089,24 +1078,15 @@ public class NewClassCodeGenerator {
private String getHeaderIncludeString(ITranslationUnit sourceTU, ITranslationUnit headerTU, private String getHeaderIncludeString(ITranslationUnit sourceTU, ITranslationUnit headerTU,
IProgressMonitor monitor) { IProgressMonitor monitor) {
IProject project = headerTU.getCProject().getProject();
IPath projectLocation = new Path(project.getLocationURI().getPath());
IPath headerLocation = new Path(headerTU.getResource().getLocationURI().getPath()); IPath headerLocation = new Path(headerTU.getResource().getLocationURI().getPath());
IPath sourceLocation = new Path(sourceTU.getResource().getLocationURI().getPath());
IPath includePath = PathUtil.makeRelativePathToProjectIncludes(headerLocation, project); InclusionContext inclusionContext = new InclusionContext(sourceTU);
boolean isSystemIncludePath = false; IncludeInfo includeInfo = inclusionContext.getIncludeForHeaderFile(headerLocation);
if (headerTU.getResource() == null && includePath != null if (includeInfo == null) {
&& !projectLocation.isPrefixOf(headerLocation)) { includeInfo = new IncludeInfo(headerLocation.toString(), false);
isSystemIncludePath = true;
} else if (projectLocation.isPrefixOf(headerLocation)
&& projectLocation.isPrefixOf(sourceLocation)) {
includePath = PathUtil.makeRelativePath(headerLocation, sourceLocation.removeLastSegments(1));
} }
if (includePath == null)
includePath = headerLocation; return "#include " + includeInfo.toString(); //$NON-NLS-1$
return getIncludeString(includePath.toString(), isSystemIncludePath);
} }
private boolean hasInclude(String contents, String include) { private boolean hasInclude(String contents, String include) {
@ -1208,21 +1188,6 @@ public class NewClassCodeGenerator {
return text.toString(); return text.toString();
} }
private String getIncludeString(String fileName, boolean isSystemInclude) {
StringBuilder buf = new StringBuilder();
buf.append("#include "); //$NON-NLS-1$
if (isSystemInclude)
buf.append('<');
else
buf.append('\"');
buf.append(fileName);
if (isSystemInclude)
buf.append('>');
else
buf.append('\"');
return buf.toString();
}
private int findLastLineChar(String contents, int startPos) { private int findLastLineChar(String contents, int startPos) {
int endPos = contents.length() - 1; int endPos = contents.length() - 1;
int linePos = startPos; int linePos = startPos;