1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-07 17:56:01 +02:00

Bug 414624 - Add Include command doesn't honor Organize Includes

preferences
This commit is contained in:
Sergey Prigogin 2013-08-13 20:43:34 -07:00
parent 7ae8b48608
commit ca5586f8be
31 changed files with 847 additions and 726 deletions

View file

@ -62,7 +62,7 @@ Export-Package: org.eclipse.cdt.core,
org.eclipse.cdt.internal.core.dom.rewrite.astwriter;x-friends:="org.eclipse.cdt.ui",
org.eclipse.cdt.internal.core.dom.rewrite.changegenerator;x-internal:=true,
org.eclipse.cdt.internal.core.dom.rewrite.commenthandler;x-friends:="org.eclipse.cdt.ui",
org.eclipse.cdt.internal.core.dom.rewrite.util;x-internal:=true,
org.eclipse.cdt.internal.core.dom.rewrite.util;x-friends:="org.eclipse.cdt.ui",
org.eclipse.cdt.internal.core.envvar;x-friends:="org.eclipse.cdt.ui,org.eclipse.cdt.managedbuilder.core",
org.eclipse.cdt.internal.core.index;x-friends:="org.eclipse.cdt.ui",
org.eclipse.cdt.internal.core.index.composite;x-internal:=true,

View file

@ -25,7 +25,6 @@ import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.ToolFactory;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
@ -547,7 +546,7 @@ public class ChangeGenerator extends ASTVisitor {
if (ASTWriter.requireBlankLineInBetween(newNode, anchorNode)) {
writer.newLine();
}
int insertPos = getOffsetIncludingComments(anchorNode);
int insertPos = commentMap.getOffsetIncludingComments(anchorNode);
int length = 0;
if (writer.getScribe().isAtBeginningOfLine()) {
String tuCode = anchorNode.getTranslationUnit().getRawSignature();
@ -573,8 +572,8 @@ public class ChangeGenerator extends ASTVisitor {
addToRootEdit(node);
if (modifications.size() == 1 && modifications.get(0).getNewNode() == null) {
// There is no replacement. We are deleting a piece of existing code.
int offset = getOffsetIncludingComments(node);
int endOffset = getEndOffsetIncludingComments(node);
int offset = commentMap.getOffsetIncludingComments(node);
int endOffset = commentMap.getEndOffsetIncludingComments(node);
offset = Math.max(skipPrecedingBlankLines(source, offset), processedOffset);
endOffset = skipTrailingBlankLines(source, endOffset);
IASTNode[] siblingsList = getContainingNodeList(node);
@ -615,7 +614,7 @@ public class ChangeGenerator extends ASTVisitor {
addChildEdit(new ReplaceEdit(offset, endOffset - offset, code));
if (node instanceof IASTStatement || node instanceof IASTDeclaration) {
// Include trailing comments in the area to be replaced.
int commentEnd = getEndOffsetIncludingTrailingComments(node);
int commentEnd = commentMap.getEndOffsetIncludingComments(node);
if (commentEnd > endOffset)
addChildEdit(new DeleteEdit(endOffset, commentEnd - endOffset));
}
@ -669,7 +668,7 @@ public class ChangeGenerator extends ASTVisitor {
prevNode = preprocessorStatements[preprocessorStatements.length - 1];
}
}
int offset = prevNode != null ? getEndOffsetIncludingComments(prevNode) : 0;
int offset = prevNode != null ? commentMap.getEndOffsetIncludingComments(prevNode) : 0;
String source = node.getRawSignature();
int endOffset = skipTrailingBlankLines(source, offset);
@ -1015,69 +1014,6 @@ public class ChangeGenerator extends ASTVisitor {
}
}
private int getOffsetIncludingComments(IASTNode node) {
int nodeOffset = offset(node);
List<IASTComment> comments = commentMap.getAllCommentsForNode(node);
if (!comments.isEmpty()) {
int startOffset = nodeOffset;
for (IASTComment comment : comments) {
int commentOffset = offset(comment);
if (commentOffset < startOffset) {
startOffset = commentOffset;
}
}
nodeOffset = startOffset;
}
return nodeOffset;
}
private int getEndOffsetIncludingComments(IASTNode node) {
int endOffset = 0;
while (true) {
IASTFileLocation fileLocation = node.getFileLocation();
if (fileLocation != null)
endOffset = Math.max(endOffset, endOffset(fileLocation));
List<IASTComment> comments = commentMap.getAllCommentsForNode(node);
if (!comments.isEmpty()) {
for (IASTComment comment : comments) {
int commentEndOffset = endOffset(comment);
if (commentEndOffset >= endOffset) {
endOffset = commentEndOffset;
}
}
}
IASTNode[] children = node.getChildren();
if (children.length == 0)
break;
node = children[children.length - 1];
}
return endOffset;
}
private int getEndOffsetIncludingTrailingComments(IASTNode node) {
int endOffset = 0;
while (true) {
IASTFileLocation fileLocation = node.getFileLocation();
if (fileLocation != null)
endOffset = Math.max(endOffset, endOffset(fileLocation));
List<IASTComment> comments = commentMap.getTrailingCommentsForNode(node);
if (!comments.isEmpty()) {
for (IASTComment comment : comments) {
int commentEndOffset = endOffset(comment);
if (commentEndOffset >= endOffset) {
endOffset = commentEndOffset;
}
}
}
IASTNode[] children = node.getChildren();
if (children.length == 0)
break;
node = children[children.length - 1];
}
return endOffset;
}
private Map<ModificationKind, List<ASTModification>> getModifications(IASTNode node) {
Map<ModificationKind, List<ASTModification>> modifications = classifiedModifications.get(node);
if (modifications == null)

View file

@ -17,7 +17,9 @@ import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.internal.core.dom.rewrite.util.ASTNodes;
/**
* The NodeCommentMap is the map where all the comments are assigned to a node. For better
@ -147,4 +149,43 @@ public class NodeCommentMap {
comment.addAll(getTrailingCommentsForNode(node));
return comment;
}
public int getOffsetIncludingComments(IASTNode node) {
int offset = ASTNodes.offset(node);
// TODO(sprigogin): Iterate backwards and stop at the first blank line.
List<IASTComment> comments = leadingMap.get(node);
if (comments != null && !comments.isEmpty()) {
for (IASTComment comment : comments) {
int commentOffset = ASTNodes.offset(comment);
if (commentOffset < offset) {
offset = commentOffset;
}
}
}
return offset;
}
public int getEndOffsetIncludingComments(IASTNode node) {
int endOffset = 0;
while (true) {
IASTFileLocation fileLocation = node.getFileLocation();
if (fileLocation != null)
endOffset = Math.max(endOffset, fileLocation.getNodeOffset() + fileLocation.getNodeLength());
List<IASTComment> comments = trailingMap.get(node);
if (comments != null && !comments.isEmpty()) {
for (IASTComment comment : comments) {
int commentEndOffset = ASTNodes.endOffset(comment);
if (commentEndOffset >= endOffset) {
endOffset = commentEndOffset;
}
}
}
IASTNode[] children = node.getChildren();
if (children.length == 0)
break;
node = children[children.length - 1];
}
return endOffset;
}
}

View file

@ -8,7 +8,9 @@
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.corext.util;
package org.eclipse.cdt.internal.core.dom.rewrite.util;
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeEndOffset;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTNode;
@ -17,8 +19,7 @@ import org.eclipse.cdt.core.dom.ast.IASTNode;
* Collection of helper methods for common operations on AST nodes.
*/
public class ASTNodes {
// Not instantiatable.
/** Not instantiatable. */
private ASTNodes() {
}
@ -36,4 +37,12 @@ public class ASTNodes {
IASTFileLocation location = node.getFileLocation();
return location.getNodeOffset() + location.getNodeLength();
}
/**
* Returns the offset of the beginning of the next line after the node, or the end-of-file
* offset if there is no line delimiter after the node.
*/
public static int skipToNextLineAfterNode(char[] text, IASTNode node) {
return TextUtil.skipToNextLine(text, getNodeEndOffset(node));
}
}

View file

@ -0,0 +1,73 @@
/*******************************************************************************
* Copyright (c) 2013 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite.util;
/**
* Collection of methods for working with text.
*/
public class TextUtil {
/** Not instantiatable. */
private TextUtil() {}
/**
* Returns the offset of the beginning of the next line after the given offset,
* or the end-of-file offset if there is no line delimiter after the given offset.
*/
public static int skipToNextLine(char[] text, int offset) {
while (offset < text.length) {
if (text[offset++] == '\n')
break;
}
return offset;
}
/**
* Returns the offset of the beginning of the line containing the given offset.
*/
public static int getLineStart(char[] text, int offset) {
while (--offset >= 0) {
if (text[offset] == '\n')
break;
}
return offset + 1;
}
/**
* Skips whitespace characters to the left of the given offset without leaving the current line.
*/
public static int skipWhitespaceToTheLeft(char[] text, int offset) {
while (--offset >= 0) {
char c = text[offset];
if (c == '\n' || !Character.isWhitespace(c))
break;
}
return offset + 1;
}
/**
* Returns {@code true} the line prior to the line corresponding to the given {@code offset}
* does not contain non-whitespace characters.
*/
public static boolean isPreviousLineBlank(char[] text, int offset) {
while (--offset >= 0) {
if (text[offset] == '\n')
break;
}
while (--offset >= 0) {
char c = text[offset];
if (c == '\n')
return true;
if (!Character.isWhitespace(c))
return false;
}
return false;
}
}

View file

@ -1,6 +1,7 @@
#include "A.h"
#include "OverloadedFunction.h"
#include "A.h"
using ns3::func;
void test() {

View file

@ -1,6 +1,7 @@
#include "ResolvedName.h"
#include "UnresolvedName.h"
#include "ResolvedName.h"
using ns4::A;
using ns5::B;

View file

@ -1,6 +1,7 @@
#include "VariableTypeHelper.h"
#include "VariableType.h"
#include "VariableTypeHelper.h"
namespace ns2 {
void VT::method() {

View file

@ -11,6 +11,7 @@
*******************************************************************************/
package org.eclipse.cdt.ui.tests.text;
import java.util.Collection;
import java.util.ListResourceBundle;
import junit.extensions.TestSetup;
@ -31,8 +32,9 @@ import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.testplugin.EditorTestHelper;
import org.eclipse.cdt.ui.testplugin.ResourceTestHelper;
import org.eclipse.cdt.internal.ui.editor.AddIncludeOnSelectionAction;
import org.eclipse.cdt.internal.ui.editor.AddIncludeAction;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.refactoring.includes.IElementSelector;
/**
* Tests the AddIncludeOnSelectionAction.
@ -94,8 +96,20 @@ public class AddIncludeTest extends BaseTestCase {
}
private void assertAddIncludeResult() throws Exception {
AddIncludeOnSelectionAction.sIsJUnitTest= true;
new AddIncludeOnSelectionAction(fEditor).run();
AddIncludeAction action = new AddIncludeAction(fEditor);
action.setAmbiguityResolver(new IElementSelector() {
@Override
public <T> T selectElement(Collection<T> elements) {
switch (elements.size()) {
case 0:
return null;
case 1:
return elements.iterator().next();
}
throw new RuntimeException("Ambiguous input: " + elements); //$NON-NLS-1$
}
});
action.run();
String file= createFileName(".expected");
String expected= ResourceTestHelper.read(file).toString();

View file

@ -1,272 +0,0 @@
/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* QNX Software Systems
* Sergey Prigogin (Google)
* Markus Schorn (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.codemanipulation;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.IBuffer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.IMacro;
import org.eclipse.cdt.core.model.ISourceRange;
import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.ui.editor.CEditorMessages;
/**
* Adds includes and 'using' declarations to a translation unit.
* If the translation unit is open in an editor, be sure to pass over its working copy.
*/
public class AddIncludesOperation implements IWorkspaceRunnable {
private final ITranslationUnit fTranslationUnit;
private final int fBeforeOffset;
private final List<IncludeInfo> fIncludes;
private final List<String> fUsings;
private String fNewLine;
private IBuffer fBuffer;
private List<ICElement> fExistingIncludes;
private List<ICElement> fExistingUsings;
private InsertEdit fIncludesInsert;
private InsertEdit fUsingsInsert;
private int fIncludesPos= -1;
/**
* @param tu a translation unit.
* @param beforeOffset includes and 'using' declarations have to be inserted before this offset.
* @param includes '#include' statements to insert.
* @param usings 'using' statements to insert.
*/
public AddIncludesOperation(ITranslationUnit tu, int beforeOffset, List<IncludeInfo> includes,
List<String> usings) {
fTranslationUnit = tu;
fBeforeOffset = beforeOffset;
fIncludes= includes;
fUsings = usings;
}
/**
* @return Returns the scheduling rule for this operation
*/
public ISchedulingRule getSchedulingRule() {
return ResourcesPlugin.getWorkspace().getRoot();
}
@Override
public void run(IProgressMonitor monitor) throws CoreException {
if (monitor == null) {
monitor= new NullProgressMonitor();
}
try {
monitor.beginTask(CEditorMessages.AddIncludesOperation_description, 3);
fBuffer = fTranslationUnit.getBuffer();
fNewLine= getLineSeparator();
fExistingIncludes = fTranslationUnit.getChildrenOfType(ICElement.C_INCLUDE);
fIncludesInsert = getIncludesInsert();
monitor.worked(1);
if (!fUsings.isEmpty()) {
fExistingUsings = fTranslationUnit.getChildrenOfType(ICElement.C_USING);
}
fUsingsInsert = getUsingsInsert();
monitor.worked(1);
if (fIncludesInsert != null) {
fBuffer.replace(fIncludesInsert.getOffset(), 0, fIncludesInsert.getText());
}
if (fUsingsInsert != null) {
int offset = fUsingsInsert.getOffset();
if (fIncludesInsert != null && offset >= fIncludesInsert.getOffset()) {
offset += fIncludesInsert.getText().length();
}
fBuffer.replace(offset, 0, fUsingsInsert.getText());
}
monitor.worked(1);
} finally {
monitor.done();
}
}
private InsertEdit getIncludesInsert() throws CoreException {
if (fIncludes.isEmpty()) {
return null;
}
ArrayList<IncludeInfo> toAdd = new ArrayList<IncludeInfo>();
for (IncludeInfo include : fIncludes) {
String name = include.getName();
boolean found = false;
for (ICElement element : fExistingIncludes) {
ISourceRange range = ((ISourceReference) element).getSourceRange();
if (range.getStartPos() + range.getLength() > fBeforeOffset) {
break;
}
if (name.equals(element.getElementName())) {
found = true;
break;
}
}
if (!found) {
toAdd.add(include);
}
}
if (toAdd.isEmpty()) {
return null;
}
// So we have our list. Now insert.
StringBuilder buf = new StringBuilder();
for (IncludeInfo include : toAdd) {
buf.append("#include ").append(include.toString()).append(fNewLine); //$NON-NLS-1$
}
int pos= getIncludeInsertionPosition();
return new InsertEdit(pos, buf.toString());
}
private int getIncludeInsertionPosition() throws CModelException {
if (fIncludesPos < 0) {
if (fExistingIncludes.isEmpty()) {
fIncludesPos= getOffsetAfterLeadingMacroDefinitions();
} else {
fIncludesPos = getOffsetAfterLast(fExistingIncludes);
}
}
return fIncludesPos;
}
private InsertEdit getUsingsInsert() throws CoreException {
if (fUsings.isEmpty()) {
return null;
}
ArrayList<String> toAdd = new ArrayList<String>(fUsings.size());
for (String name : fUsings) {
boolean found = false;
for (ICElement element : fExistingUsings) {
ISourceRange range = ((ISourceReference) element).getSourceRange();
if (range.getStartPos() + range.getLength() > fBeforeOffset) {
break;
}
if (name.equals(element.getElementName())) {
found = true;
break;
}
}
if (!found) {
toAdd.add(name);
}
}
if (toAdd.isEmpty()) {
return null;
}
// So we have our list. Now insert.
StringBuilder buf = new StringBuilder();
for (String using : toAdd) {
buf.append("using ").append(using).append(';').append(fNewLine); //$NON-NLS-1$
}
int pos = getOffsetAfterLast(fExistingUsings);
int pos2 = getIncludeInsertionPosition();
if (pos <= pos2) {
pos = pos2;
buf.insert(0, fNewLine); // Add a blank line between #include and using statements.
}
return new InsertEdit(pos, buf.toString());
}
/**
* Find the last of elements located before fBeforeOffset and returns offset of the following line.
* @param elements source elements to consider.
* @return offset of the line after the last of elements located before fBeforeOffset, or
* zero, if there is no such element.
* @throws CModelException
*/
private int getOffsetAfterLast(List<ICElement> elements) throws CModelException {
for (int i = elements.size(); --i >= 0;) {
ISourceRange range = ((ISourceReference) elements.get(i)).getSourceRange();
int end = range.getStartPos() + range.getLength();
if (end <= fBeforeOffset) {
return findNewLine(range.getStartPos() + range.getLength());
}
}
return 0;
}
/**
* Find the last leading macro definition before <code>fBeforeOffset</code>.
* And returns the offset of the line after.
*/
private int getOffsetAfterLeadingMacroDefinitions() throws CModelException {
ISourceRange found= null;
for (ICElement child: fTranslationUnit.getChildren()) {
if (!(child instanceof IMacro) || !(child instanceof ISourceReference))
break;
final ISourceReference sourceRef = (ISourceReference) child;
if (!sourceRef.isActive())
break;
ISourceRange range= sourceRef.getSourceRange();
if (range.getStartPos() + range.getLength() > fBeforeOffset)
break;
found= range;
}
if (found != null) {
return findNewLine(found.getStartPos() + found.getLength());
}
return 0;
}
private int findNewLine(int pos) {
while (fBuffer.getChar(pos) != '\n') {
pos++;
}
if (fBuffer.getChar(pos) == '\r') {
pos++;
}
return pos + 1;
}
private String getLineSeparator() {
try {
if (fBuffer instanceof IAdaptable) {
IDocument doc= (IDocument) ((IAdaptable) fBuffer).getAdapter(IDocument.class);
if (doc != null) {
String delim= doc.getLineDelimiter(0);
if (delim != null) {
return delim;
}
}
}
} catch (BadLocationException e) {
}
return System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$
}
}

View file

@ -89,6 +89,10 @@ public class IncludeInfo implements Comparable<IncludeInfo> {
return (isSystem ? '<' : '"') + name + (isSystem ? '>' : '"');
}
public String composeIncludeStatement() {
return "#include " + toString(); //$NON-NLS-1$
}
@Override
public int compareTo(IncludeInfo other) {
if (isSystem != other.isSystem)

View file

@ -19,7 +19,7 @@ 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> {
public class StyledInclude {
private final IPath header; // null for existing unresolved includes
private final IncludeInfo includeInfo; // never null
private final IncludeGroupStyle style;
@ -66,11 +66,6 @@ public class StyledInclude implements Comparable<StyledInclude> {
this.existingInclude = existingInclude;
}
@Override
public int compareTo(StyledInclude other) {
return includeInfo.compareTo(other.includeInfo);
}
@Override
public int hashCode() {
if (header != null)

View file

@ -39,7 +39,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.corext.util.ASTNodes;
import org.eclipse.cdt.internal.core.dom.rewrite.util.ASTNodes;
public class InputFlowAnalyzer extends FlowAnalyzer {

View file

@ -0,0 +1,194 @@
/*******************************************************************************
* Copyright (c) 2013 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.editor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.undo.DocumentUndoManagerRegistry;
import org.eclipse.text.undo.IDocumentUndoManager;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.TextEditorAction;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.SharedASTJob;
import org.eclipse.cdt.internal.ui.BusyCursorJobRunner;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
import org.eclipse.cdt.internal.ui.refactoring.includes.IElementSelector;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeCreator;
/**
* Organizes the include directives and forward declarations of a source or header file.
*/
public class AddIncludeAction extends TextEditorAction {
private IElementSelector fAmbiguityResolver;
/**
* Constructor
* @param editor The editor on which this Add Include action should operate.
*/
public AddIncludeAction(ITextEditor editor) {
super(CEditorMessages.getBundleForConstructedKeys(), "AddInclude.", editor); //$NON-NLS-1$
CUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(this, ICHelpContextIds.ADD_INCLUDE_ON_SELECTION_ACTION);
final Shell shell = editor.getEditorSite().getShell();
fAmbiguityResolver = new IElementSelector() {
@SuppressWarnings("unchecked")
@Override
public <T> T selectElement(final Collection<T> elements) {
final Object[] result = new Object[1];
runInUIThread(new Runnable() {
@Override
public void run() {
ElementListSelectionDialog dialog=
new ElementListSelectionDialog(shell, new LabelProvider());
dialog.setElements(elements.toArray());
dialog.setTitle(CEditorMessages.AddInclude_label);
dialog.setMessage(CEditorMessages.AddInclude_description);
if (dialog.open() == Window.OK)
result[0] = dialog.getFirstResult();
}
});
return (T) result[0];
}
};
}
@Override
public void run() {
final ITextEditor editor = getTextEditor();
final ITranslationUnit tu = getTranslationUnit(editor);
if (tu == null) {
return;
}
final ISelection selection= getTextEditor().getSelectionProvider().getSelection();
if (selection.isEmpty() || !(selection instanceof ITextSelection)) {
return;
}
if (!validateEditorInputState()) {
return;
}
final String lineDelimiter = getLineDelimiter(editor);
final List<TextEdit> edits = new ArrayList<TextEdit>();
SharedASTJob job = new SharedASTJob(CEditorMessages.AddInclude_action, tu) {
@Override
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
IIndex index= CCorePlugin.getIndexManager().getIndex(tu.getCProject(),
IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT);
try {
index.acquireReadLock();
IncludeCreator creator = new IncludeCreator(tu, index, lineDelimiter, fAmbiguityResolver);
edits.addAll(creator.createInclude(ast, (ITextSelection) selection));
return Status.OK_STATUS;
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} finally {
index.releaseReadLock();
}
}
};
IStatus status = BusyCursorJobRunner.execute(job);
if (status.isOK()) {
if (!edits.isEmpty()) {
// Apply text edits.
MultiTextEdit edit = new MultiTextEdit();
edit.addChildren(edits.toArray(new TextEdit[edits.size()]));
IEditorInput editorInput = editor.getEditorInput();
IDocument document = editor.getDocumentProvider().getDocument(editorInput);
IDocumentUndoManager manager= DocumentUndoManagerRegistry.getDocumentUndoManager(document);
manager.beginCompoundChange();
try {
edit.apply(document);
} catch (MalformedTreeException e) {
CUIPlugin.log(e);
} catch (BadLocationException e) {
CUIPlugin.log(e);
}
manager.endCompoundChange();
}
} else if (status.matches(IStatus.ERROR)) {
ErrorDialog.openError(editor.getEditorSite().getShell(),
CEditorMessages.AddInclude_error_title,
CEditorMessages.AddInclude_insertion_failed, status);
}
}
private static void runInUIThread(Runnable runnable) {
if (Display.getCurrent() != null) {
runnable.run();
} else {
Display.getDefault().syncExec(runnable);
}
}
private static String getLineDelimiter(ITextEditor editor) {
try {
IEditorInput editorInput = editor.getEditorInput();
IDocument document = editor.getDocumentProvider().getDocument(editorInput);
String delim= document.getLineDelimiter(0);
if (delim != null) {
return delim;
}
} catch (BadLocationException e) {
}
return System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$
}
@Override
public void update() {
ITextEditor editor = getTextEditor();
setEnabled(editor != null && getTranslationUnit(editor) != null);
}
/**
* Returns the translation unit of the given editor.
* @param editor The editor.
* @return The translation unit.
*/
private static ITranslationUnit getTranslationUnit(ITextEditor editor) {
if (editor == null) {
return null;
}
return CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput());
}
/**
* For tests only.
*/
public void setAmbiguityResolver(IElementSelector fAmbiguityResolver) {
this.fAmbiguityResolver = fAmbiguityResolver;
}
}

View file

@ -228,7 +228,7 @@ public class CEditorActionContributor extends TextEditorActionContributor {
bars.setGlobalActionHandler(CdtActionConstants.ADD_BLOCK_COMMENT, getAction(textEditor, "AddBlockComment")); //$NON-NLS-1$
bars.setGlobalActionHandler(CdtActionConstants.REMOVE_BLOCK_COMMENT, getAction(textEditor, "RemoveBlockComment")); //$NON-NLS-1$
bars.setGlobalActionHandler(CdtActionConstants.INDENT, getAction(textEditor, "Indent")); //$NON-NLS-1$
bars.setGlobalActionHandler(CdtActionConstants.ADD_INCLUDE, getAction(textEditor, "AddIncludeOnSelection")); //$NON-NLS-1$
bars.setGlobalActionHandler(CdtActionConstants.ADD_INCLUDE, getAction(textEditor, "AddInclude")); //$NON-NLS-1$
bars.setGlobalActionHandler(CdtActionConstants.ORGANIZE_INCLUDES, getAction(textEditor, "OrganizeIncludes")); //$NON-NLS-1$
bars.setGlobalActionHandler(CdtActionConstants.SORT_LINES, getAction(textEditor, "SortLines")); //$NON-NLS-1$

View file

@ -9,6 +9,7 @@
* QNX Software Systems - Initial API and implementation
* Tomasz Wesolowski
* Alvaro Sanchez-Leon (Ericsson AB)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.editor;
@ -31,12 +32,11 @@ public final class CEditorMessages extends NLS {
return fgBundleForConstructedKeys;
}
public static String AddIncludeOnSelection_label;
public static String AddIncludeOnSelection_description;
public static String AddIncludeOnSelection_error_title;
public static String AddIncludeOnSelection_insertion_failed;
public static String AddIncludeOnSelection_help_provider_error;
public static String AddIncludesOperation_description;
public static String AddInclude_label;
public static String AddInclude_description;
public static String AddInclude_action;
public static String AddInclude_error_title;
public static String AddInclude_insertion_failed;
public static String OrganizeIncludes_label;
public static String OrganizeIncludes_description;
public static String OrganizeIncludes_action;

View file

@ -22,6 +22,11 @@ AddIncludeOnSelection_error_title=Error Adding Include
AddIncludeOnSelection_insertion_failed=Adding include statements failed
AddIncludeOnSelection_help_provider_error=Help provider error
AddIncludesOperation_description=Adding include statement
AddInclude_label=Add Include
AddInclude_description=Add include statement for selected name
AddInclude_action=Adding include statement
ADdInclude_error_title=Error Adding Include Statement
AddInclude_insertion_failed=Adding include statement failed
OrganizeIncludes_label=Organize Includes
OrganizeIncludes_description=Organize includes for current file

View file

@ -1,5 +1,5 @@
###############################################################################
# Copyright (c) 2005, 2010 IBM Corporation and others.
# Copyright (c) 2005, 2013 IBM Corporation and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
@ -14,9 +14,9 @@
# Tomasz Wesolowski
###############################################################################
AddIncludeOnSelection.label=A&dd Include
AddIncludeOnSelection.description=Add include statement for selected name
AddIncludeOnSelection.tooltip=Adds an include statement for selected name
AddInclude.label=A&dd Include
AddInclude.tooltip=Add Include Statement for Selected Name
AddInclude.description=Adds an include statement for selected name
OrganizeIncludes.label=Or&ganize Includes
OrganizeIncludes.tooltip=Evaluate All Required Includes and Replace the Current Ones
@ -27,11 +27,11 @@ SortLines.tooltip=Sort Selected Lines Alphabetically
SortLines.description=Sorts selected lines alphabetically
OpenOutline.label= Quick Out&line
OpenOutline.tooltip= Shows the Quick Outline of Editor Input
OpenOutline.tooltip= Show the Quick Outline of Editor Input
OpenOutline.description= Shows the quick outline for the editor input
OpenHierarchy.label= Quick Type Hierarchy
OpenHierarchy.tooltip= Shows the Quick Type Hierarchy of Editor Input
OpenHierarchy.tooltip= Show the Quick Type Hierarchy of Editor Input
OpenHierarchy.description= Shows the quick type hierarchy for the editor input
TogglePresentation.label=Show Source of Selected Element Only

View file

@ -94,7 +94,7 @@ public class IncludeStyleBlock extends TabConfigurationBlock {
IncludeKind includeKind = style.getIncludeKind();
Key key = KEY_MAP.get(includeKind);
if (includeKind != IncludeKind.MATCHING_PATTERN) {
setValue(key, style.toString());
setValue(key, style.toXmlString());
} else {
// TODO(sprigogin): Support custom include categories.
}
@ -111,7 +111,7 @@ public class IncludeStyleBlock extends TabConfigurationBlock {
IncludeGroupStyle style = null;
String str = getValue(entry.getValue());
if (str != null)
style = IncludeGroupStyle.fromString(str, includeKind);
style = IncludeGroupStyle.fromXmlString(str, includeKind);
if (style == null)
style = new IncludeGroupStyle(includeKind);
styles.add(style);

View file

@ -46,12 +46,12 @@ import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVariableReadWriteFlags;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.rewrite.util.ASTNodes;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.cdt.internal.corext.refactoring.code.flow.FlowContext;
import org.eclipse.cdt.internal.corext.refactoring.code.flow.FlowInfo;
import org.eclipse.cdt.internal.corext.refactoring.code.flow.InputFlowAnalyzer;
import org.eclipse.cdt.internal.corext.refactoring.code.flow.Selection;
import org.eclipse.cdt.internal.corext.util.ASTNodes;
public class NodeContainer {
private final List<IASTNode> nodes;

View file

@ -391,22 +391,6 @@ public class BindingClassifier {
declareBinding(typeBinding);
}
/**
* Adds the given type to the list of bindings which have to be defined. Typedefs and
* enumerations without fixed underlying type are skipped since they must be defined in the file
* that references them by name. If the type is explicitly referenced in this translation unit,
* it will be defined independently from this method.
*
* @param type The type to add.
*/
private void defineTypeExceptTypedefOrNonFixedEnum(IType type) {
IBinding typeBinding = getTypeBinding(type);
if (typeBinding != null && !(typeBinding instanceof ITypedef)
&& !isEnumerationWithoutFixedUnderlyingType(typeBinding)) {
defineBinding(typeBinding);
}
}
/**
* Adds the given type to the list of bindings which have to be declared. Typedefs and
* enumerations without fixed underlying type are skipped since they must be defined in the file
@ -423,6 +407,22 @@ public class BindingClassifier {
}
}
/**
* Adds the given type to the list of bindings which have to be defined. Typedefs and
* enumerations without fixed underlying type are skipped since they must be defined in the file
* that references them by name. If the type is explicitly referenced in this translation unit,
* it will be defined independently from this method.
*
* @param type The type to add.
*/
private void defineTypeExceptTypedefOrNonFixedEnum(IType type) {
IBinding typeBinding = getTypeBinding(type);
if (typeBinding != null && !(typeBinding instanceof ITypedef)
&& !isEnumerationWithoutFixedUnderlyingType(typeBinding)) {
defineBinding(typeBinding);
}
}
/**
* Adds the given binding to the list of bindings which have to be defined.
*

View file

@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (c) 2013 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes;
import java.util.Collection;
/**
* Interface for selecting one of a set of elements.
*/
public interface IElementSelector {
/**
* Selects one element from a set of elements.
*
* @param elements the objects to select from
* @return the selected element or {@code null} if nothing was selected
*/
public <T> T selectElement(Collection<T> elements);
}

View file

@ -11,17 +11,20 @@
* Markus Schorn (Wind River Systems)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.editor;
package org.eclipse.cdt.internal.ui.refactoring.includes;
import static org.eclipse.cdt.core.index.IndexLocationFactory.getAbsolutePath;
import static org.eclipse.cdt.internal.ui.refactoring.includes.IncludeUtil.isContainedInRegion;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -30,26 +33,24 @@ import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.TextEditorAction;
import com.ibm.icu.text.Collator;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
@ -71,148 +72,55 @@ import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexInclude;
import org.eclipse.cdt.core.index.IIndexMacro;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.Keywords;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.IFunctionSummary;
import org.eclipse.cdt.ui.IRequiredInclude;
import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
import org.eclipse.cdt.ui.text.SharedASTJob;
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.rewrite.commenthandler.ASTCommenter;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap;
import org.eclipse.cdt.internal.core.dom.rewrite.util.ASTNodes;
import org.eclipse.cdt.internal.core.model.ASTStringUtil;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
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.corext.codemanipulation.StyledInclude;
import org.eclipse.cdt.internal.ui.CHelpProviderManager;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
import org.eclipse.cdt.internal.ui.actions.WorkbenchRunnableAdapter;
import org.eclipse.cdt.internal.ui.util.ExceptionHandler;
/**
* Adds an include statement and, optionally, a 'using' declaration for the currently
* selected name.
*/
public class AddIncludeOnSelectionAction extends TextEditorAction {
public static boolean sIsJUnitTest = false;
public class IncludeCreator {
private static final Collator COLLATOR = Collator.getInstance();
private ITranslationUnit fTu;
private IProject fProject;
private final List<IncludeInfo> fRequiredIncludes = new ArrayList<IncludeInfo>();
private final List<String> fUsingDeclarations = new ArrayList<String>();
protected InclusionContext fContext;
private final String fLineDelimiter;
private final IElementSelector fAmbiguityResolver;
private final IncludeCreationContext fContext;
public AddIncludeOnSelectionAction(ITextEditor editor) {
super(CEditorMessages.getBundleForConstructedKeys(), "AddIncludeOnSelection.", editor); //$NON-NLS-1$
CUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(this,
ICHelpContextIds.ADD_INCLUDE_ON_SELECTION_ACTION);
public IncludeCreator(ITranslationUnit tu, IIndex index, String lineDelimiter,
IElementSelector ambiguityResolver) {
fLineDelimiter = lineDelimiter;
fAmbiguityResolver = ambiguityResolver;
fContext = new IncludeCreationContext(tu, index);
}
private void insertInclude(List<IncludeInfo> includes, List<String> usings, int beforeOffset) {
AddIncludesOperation op= new AddIncludesOperation(fTu, beforeOffset, includes, usings);
try {
PlatformUI.getWorkbench().getProgressService().runInUI(
PlatformUI.getWorkbench().getProgressService(),
new WorkbenchRunnableAdapter(op), op.getSchedulingRule());
} catch (InvocationTargetException e) {
ExceptionHandler.handle(e, getShell(), CEditorMessages.AddIncludeOnSelection_error_title,
CEditorMessages.AddIncludeOnSelection_insertion_failed);
} catch (InterruptedException e) {
// Do nothing. Operation has been canceled.
}
}
private static ITranslationUnit getTranslationUnit(ITextEditor editor) {
if (editor == null) {
return null;
}
return CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput());
}
private Shell getShell() {
return getTextEditor().getSite().getShell();
}
@Override
public void run() {
fTu = getTranslationUnit(getTextEditor());
if (fTu == null) {
return;
}
fProject = fTu.getCProject().getProject();
try {
final ISelection selection= getTextEditor().getSelectionProvider().getSelection();
if (selection.isEmpty() || !(selection instanceof ITextSelection)) {
return;
}
if (!validateEditorInputState()) {
return;
}
final String[] lookupName = new String[1];
final IIndex index= CCorePlugin.getIndexManager().getIndex(fTu.getCProject(), IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT);
SharedASTJob job = new SharedASTJob(CEditorMessages.AddIncludeOnSelection_label, fTu) {
@Override
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
deduceInclude((ITextSelection) selection, index, ast, lookupName);
return Status.OK_STATUS;
}
};
job.schedule();
job.join();
if (fRequiredIncludes.isEmpty() && lookupName[0].length() > 0) {
// Try contribution from plug-ins.
IFunctionSummary fs = findContribution(lookupName[0]);
if (fs != null) {
IRequiredInclude[] functionIncludes = fs.getIncludes();
if (functionIncludes != null) {
for (IRequiredInclude include : functionIncludes) {
fRequiredIncludes.add(new IncludeInfo(include.getIncludeName(), include.isStandard()));
}
}
String ns = fs.getNamespace();
if (ns != null && ns.length() > 0) {
fUsingDeclarations.add(fs.getNamespace());
}
}
}
if (!fRequiredIncludes.isEmpty()) {
insertInclude(fRequiredIncludes, fUsingDeclarations, ((ITextSelection) selection).getOffset());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (CoreException e) {
CUIPlugin.log("Cannot perform 'Add Include'", e); //$NON-NLS-1$
}
}
/**
* Extracts the includes for the given selection. This can be both used to perform
* the work as well as being invoked when there is a change.
* @param selection a text selection.
* @param ast an AST.
* @param lookupName a one-element array used to return the selected name.
*/
private void deduceInclude(ITextSelection selection, IIndex index, IASTTranslationUnit ast, String[] lookupName)
public List<TextEdit> createInclude(IASTTranslationUnit ast, ITextSelection selection)
throws CoreException {
fContext = new InclusionContext(fTu);
IASTNodeSelector selector = ast.getNodeSelector(fTu.getLocation().toOSString());
ITranslationUnit tu = fContext.getTranslationUnit();
IASTNodeSelector selector = ast.getNodeSelector(tu.getLocation().toOSString());
IASTName name = selector.findEnclosingName(selection.getOffset(), selection.getLength());
if (name == null) {
return;
return Collections.emptyList();
}
char[] nameChars = name.toCharArray();
lookupName[0] = new String(nameChars);
String lookupName = new String(nameChars);
IBinding binding = name.resolveBinding();
if (binding instanceof ICPPVariable) {
IType type = ((ICPPVariable) binding).getType();
@ -224,13 +132,18 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
}
}
if (nameChars.length == 0) {
return;
return Collections.emptyList();
}
final Map<String, IncludeCandidate> candidatesMap= new HashMap<String, IncludeCandidate>();
final IndexFilter filter = IndexFilter.getDeclaredBindingFilter(ast.getLinkage().getLinkageID(), false);
final List<IncludeInfo> requiredIncludes = new ArrayList<IncludeInfo>();
final List<UsingDeclaration> usingDeclarations = new ArrayList<UsingDeclaration>();
List<IIndexBinding> bindings = new ArrayList<IIndexBinding>();
try {
IIndex index = fContext.getIndex();
IIndexBinding adaptedBinding= index.adaptBinding(binding);
if (adaptedBinding == null) {
bindings.addAll(Arrays.asList(index.findBindings(nameChars, false, filter, new NullProgressMonitor())));
@ -272,39 +185,221 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
final ArrayList<IncludeCandidate> candidates = new ArrayList<IncludeCandidate>(candidatesMap.values());
if (candidates.size() > 1) {
if (sIsJUnitTest) {
throw new RuntimeException("ambiguous input"); //$NON-NLS-1$
}
runInUIThread(new Runnable() {
@Override
public void run() {
ElementListSelectionDialog dialog=
new ElementListSelectionDialog(getShell(), new LabelProvider());
dialog.setElements(candidates.toArray());
dialog.setTitle(CEditorMessages.AddIncludeOnSelection_label);
dialog.setMessage(CEditorMessages.AddIncludeOnSelection_description);
if (dialog.open() == Window.OK) {
IncludeCandidate candidate = fAmbiguityResolver.selectElement(candidates);
if (candidate == null)
return Collections.emptyList();
candidates.clear();
candidates.add((IncludeCandidate) dialog.getFirstResult());
}
}
});
candidates.add(candidate);
}
fRequiredIncludes.clear();
fUsingDeclarations.clear();
if (candidates.size() == 1) {
IncludeCandidate candidate = candidates.get(0);
fRequiredIncludes.add(candidate.getInclude());
IIndexBinding indexBinding = candidate.getBinding();
requiredIncludes.add(candidate.include);
IIndexBinding indexBinding = candidate.binding;
if (indexBinding instanceof ICPPBinding && !(indexBinding instanceof IIndexMacro)) {
// Decide what 'using' declaration, if any, should be added along with the include.
String usingDeclaration = deduceUsingDeclaration(binding, indexBinding, ast);
UsingDeclaration usingDeclaration = deduceUsingDeclaration(binding, indexBinding, ast);
if (usingDeclaration != null)
fUsingDeclarations.add(usingDeclaration);
usingDeclarations.add(usingDeclaration);
}
}
} catch (CoreException e) {
CUIPlugin.log(e);
return Collections.emptyList();
}
if (requiredIncludes.isEmpty() && !lookupName.isEmpty()) {
// Try contribution from plug-ins.
IFunctionSummary fs = findContribution(lookupName);
if (fs != null) {
IRequiredInclude[] functionIncludes = fs.getIncludes();
if (functionIncludes != null) {
for (IRequiredInclude include : functionIncludes) {
requiredIncludes.add(new IncludeInfo(include.getIncludeName(), include.isStandard()));
}
}
String ns = fs.getNamespace();
if (ns != null && !ns.isEmpty()) {
usingDeclarations.add(new UsingDeclaration(ns + "::" + fs.getName())); //$NON-NLS-1$
}
}
}
return createEdits(requiredIncludes, usingDeclarations, ast, selection);
}
private List<TextEdit> createEdits(List<IncludeInfo> includes,
List<UsingDeclaration> usingDeclarations, IASTTranslationUnit ast,
ITextSelection selection) {
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
char[] contents = fContext.getSourceContents();
IRegion includeRegion =
IncludeOrganizer.getSafeIncludeReplacementRegion(contents, ast, commentedNodeMap);
IncludePreferences preferences = fContext.getPreferences();
List<TextEdit> edits = new ArrayList<TextEdit>();
IASTPreprocessorIncludeStatement[] existingIncludes = ast.getIncludeDirectives();
fContext.addHeadersIncludedPreviously(existingIncludes);
List<StyledInclude> styledIncludes = new ArrayList<StyledInclude>();
// Put the new includes into styledIncludes.
for (IncludeInfo includeInfo : includes) {
IPath header = fContext.resolveInclude(includeInfo);
if (!fContext.wasIncludedPreviously(header)) {
IncludeGroupStyle style = fContext.getIncludeStyle(includeInfo);
StyledInclude prototype = new StyledInclude(header, includeInfo, style);
styledIncludes.add(prototype);
}
}
Collections.sort(styledIncludes, preferences);
// Populate list of existing includes in the include insertion region.
List<StyledInclude> mergedIncludes = new ArrayList<StyledInclude>();
for (IASTPreprocessorIncludeStatement include : existingIncludes) {
if (include.isPartOfTranslationUnitFile() && isContainedInRegion(include, includeRegion)) {
String name = new String(include.getName().getSimpleID());
IncludeInfo includeInfo = new IncludeInfo(name, include.isSystemInclude());
String path = include.getPath();
// An empty path means that the include was not resolved.
IPath header = path.isEmpty() ? null : Path.fromOSString(path);
IncludeGroupStyle style =
header != null ? fContext.getIncludeStyle(header) : fContext.getIncludeStyle(includeInfo);
StyledInclude prototype = new StyledInclude(header, includeInfo, style, include);
mergedIncludes.add(prototype);
}
}
if (preferences.allowReordering) {
// Since the order of existing include statements may not match the include order
// preferences, we find positions for the new include statements by pushing them from
// them up from the bottom of the include insertion region.
for (StyledInclude include : styledIncludes) {
int i = mergedIncludes.size();
while (--i >= 0 && preferences.compare(include, mergedIncludes.get(i)) < 0) {}
mergedIncludes.add(i + 1, include);
}
} else {
mergedIncludes.addAll(styledIncludes);
}
int offset = includeRegion.getOffset();
StringBuilder text = new StringBuilder();
StyledInclude previousInclude = null;
for (StyledInclude include : mergedIncludes) {
if (include.getExistingInclude() == null) {
if (previousInclude != null) {
IASTNode previousNode = previousInclude.getExistingInclude();
if (previousNode != null) {
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
flushEditBuffer(offset, text, edits);
if (contents[offset - 1] != '\n')
text.append(fLineDelimiter);
}
if (include.getStyle().isBlankLineNeededAfter(previousInclude.getStyle(), preferences.includeStyles))
text.append(fLineDelimiter);
}
text.append(include.getIncludeInfo().composeIncludeStatement());
text.append(fLineDelimiter);
} else {
if (previousInclude != null && previousInclude.getExistingInclude() == null &&
include.getStyle().isBlankLineNeededAfter(previousInclude.getStyle(), preferences.includeStyles)) {
text.append(fLineDelimiter);
}
flushEditBuffer(offset, text, edits);
}
previousInclude = include;
}
flushEditBuffer(offset, text, edits);
List<UsingDeclaration> mergedUsingDeclarations = getUsingDeclarations(ast);
for (UsingDeclaration usingDeclaration : mergedUsingDeclarations) {
for (Iterator<UsingDeclaration> iter = usingDeclarations.iterator(); iter.hasNext();) {
UsingDeclaration using = iter.next();
if (using.equals(usingDeclaration.name))
iter.remove();
}
}
if (usingDeclarations.isEmpty())
return edits;
List<UsingDeclaration> temp = null;
for (Iterator<UsingDeclaration> iter = mergedUsingDeclarations.iterator(); iter.hasNext();) {
UsingDeclaration usingDeclaration = iter.next();
if (usingDeclaration.existingDeclaration.isPartOfTranslationUnitFile() &&
ASTNodes.endOffset(usingDeclaration.existingDeclaration) <= selection.getOffset()) {
if (temp == null)
temp = new ArrayList<UsingDeclaration>();
temp.add(usingDeclaration);
}
}
if (temp == null) {
mergedUsingDeclarations.clear();
} else {
mergedUsingDeclarations = temp;
}
Collections.sort(usingDeclarations);
if (mergedUsingDeclarations.isEmpty()) {
offset = includeRegion.getOffset() + includeRegion.getLength();
text.append(fLineDelimiter); // Blank line between includes and using declarations.
} else {
offset = commentedNodeMap.getOffsetIncludingComments(mergedUsingDeclarations.get(0).existingDeclaration);
}
// Since the order of existing using declarations may not be alphabetical, we find positions
// for the new using declarations by pushing them from them up from the bottom of
// the using declaration list.
for (UsingDeclaration using : usingDeclarations) {
int i = mergedUsingDeclarations.size();
while (--i >= 0 && using.compareTo(mergedUsingDeclarations.get(i)) < 0) {}
mergedUsingDeclarations.add(i + 1, using);
}
UsingDeclaration previousUsing = null;
for (UsingDeclaration using : mergedUsingDeclarations) {
if (using.existingDeclaration == null) {
if (previousUsing != null) {
IASTNode previousNode = previousUsing.existingDeclaration;
if (previousNode != null) {
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
flushEditBuffer(offset, text, edits);
if (contents[offset - 1] != '\n')
text.append(fLineDelimiter);
}
}
text.append(using.composeDirective());
text.append(fLineDelimiter);
} else {
flushEditBuffer(offset, text, edits);
}
previousUsing = using;
}
flushEditBuffer(offset, text, edits);
return edits;
}
private List<UsingDeclaration> getUsingDeclarations(IASTTranslationUnit ast) {
List<UsingDeclaration> usingDeclarations = new ArrayList<UsingDeclaration>();
IASTDeclaration[] declarations = ast.getDeclarations();
for (IASTDeclaration declaration : declarations) {
if (declaration instanceof ICPPASTUsingDeclaration) {
usingDeclarations.add(new UsingDeclaration((ICPPASTUsingDeclaration) declaration));
}
}
return usingDeclarations;
}
private void flushEditBuffer(int offset, StringBuilder text, List<TextEdit> edits) {
if (text.length() != 0) {
edits.add(new InsertEdit(offset, text.toString()));
text.delete(0, text.length());
}
}
/**
@ -331,7 +426,8 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
}
}
private String deduceUsingDeclaration(IBinding source, IBinding target, IASTTranslationUnit ast) {
private UsingDeclaration deduceUsingDeclaration(IBinding source, IBinding target,
IASTTranslationUnit ast) {
if (source.equals(target)) {
return null; // No using declaration is needed.
}
@ -379,7 +475,7 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
}
buf.append(targetChain.get(i));
}
return buf.toString();
return new UsingDeclaration(buf.toString());
}
private boolean match(IASTName name, ArrayList<String> usingChain, boolean excludeLast) {
@ -438,7 +534,7 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
while (!front.isEmpty()) {
IIndexFile file = front.remove();
// A header without an extension is a good candidate for inclusion into a C++ source file.
if (fTu.isCXXLanguage() && !hasExtension(getPath(file))) {
if (fContext.isCXXLanguage() && !hasExtension(getPath(file))) {
return file;
}
IIndexInclude[] includes = index.findIncludedBy(file, 0);
@ -473,7 +569,7 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
return path.indexOf('.', path.lastIndexOf('/') + 1) >= 0;
}
private IFunctionSummary findContribution(final String name) {
private IFunctionSummary findContribution(final String name) throws CoreException {
final IFunctionSummary[] fs = new IFunctionSummary[1];
IRunnableWithProgress op = new IRunnableWithProgress() {
@Override
@ -481,12 +577,12 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
ICHelpInvocationContext context = new ICHelpInvocationContext() {
@Override
public IProject getProject() {
return fProject;
return fContext.getProject();
}
@Override
public ITranslationUnit getTranslationUnit() {
return fTu;
return fContext.getTranslationUnit();
}
};
@ -496,34 +592,21 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
try {
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(op);
} catch (InvocationTargetException e) {
ExceptionHandler.handle(e, getShell(), CEditorMessages.AddIncludeOnSelection_error_title,
CEditorMessages.AddIncludeOnSelection_help_provider_error);
throw new CoreException(CUIPlugin.createErrorStatus(Messages.AddInclude_help_provider_error, e));
} catch (InterruptedException e) {
// Do nothing. Operation has been canceled.
}
return fs[0];
}
private void runInUIThread(Runnable runnable) {
if (Display.getCurrent() != null) {
runnable.run();
} else {
Display.getDefault().syncExec(runnable);
}
}
@Override
public void update() {
ITextEditor editor = getTextEditor();
setEnabled(editor != null && getTranslationUnit(editor) != null);
}
/**
* Checks if a file is a source file (.c, .cpp, .cc, etc). Header files are not considered source files.
* @return Returns <code>true</code> if the the file is a source file.
* Checks if a file is a source file (.c, .cpp, .cc, etc). Header files are not considered
* source files.
*
* @return Returns {@code true} if the the file is a source file.
*/
private boolean isSource(String filename) {
IContentType ct= CCorePlugin.getContentType(fProject, filename);
IContentType ct= CCorePlugin.getContentType(fContext.getProject(), filename);
if (ct != null) {
String id = ct.getId();
if (CCorePlugin.CONTENT_TYPE_CSOURCE.equals(id) || CCorePlugin.CONTENT_TYPE_CXXSOURCE.equals(id)) {
@ -623,27 +706,43 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
* definitions for "add include" when there are more than one to choose from.
*/
private static class IncludeCandidate {
private final IIndexBinding binding;
private final IncludeInfo include;
private final String label;
final IIndexBinding binding;
final IncludeInfo include;
final String label;
public IncludeCandidate(IIndexBinding binding, IncludeInfo include) throws CoreException {
IncludeCandidate(IIndexBinding binding, IncludeInfo include) throws CoreException {
this.binding = binding;
this.include = include;
this.label = getBindingQualifiedName(binding) + " - " + include.toString(); //$NON-NLS-1$
}
public IIndexBinding getBinding() {
return binding;
}
public IncludeInfo getInclude() {
return include;
}
@Override
public String toString() {
return label;
}
}
private static class UsingDeclaration implements Comparable<UsingDeclaration> {
final String name;
final ICPPASTUsingDeclaration existingDeclaration;
UsingDeclaration(String name) {
this.name = name;
this.existingDeclaration = null;
}
UsingDeclaration(ICPPASTUsingDeclaration existingDeclaration) {
this.name = ASTStringUtil.getQualifiedName(existingDeclaration.getName());
this.existingDeclaration = existingDeclaration;
}
@Override
public int compareTo(UsingDeclaration other) {
return COLLATOR.compare(name, other.name);
}
String composeDirective() {
return "using " + name + ';'; //$NON-NLS-1$
}
}
}

View file

@ -227,8 +227,7 @@ public class IncludeGroupStyle implements Comparable<IncludeGroupStyle> {
memento.putInteger(TAG_ORDER, order);
}
@Override
public String toString() {
public String toXmlString() {
XMLMemento memento = XMLMemento.createWriteRoot(TAG_STYLE);
saveToMemento(memento);
StringWriter writer = new StringWriter();
@ -240,7 +239,7 @@ public class IncludeGroupStyle implements Comparable<IncludeGroupStyle> {
return writer.toString();
}
public static IncludeGroupStyle fromString(String str, IncludeKind includeKind) {
public static IncludeGroupStyle fromXmlString(String str, IncludeKind includeKind) {
StringReader reader = new StringReader(str);
XMLMemento memento;
try {
@ -251,6 +250,12 @@ public class IncludeGroupStyle implements Comparable<IncludeGroupStyle> {
return fromMemento(memento, includeKind);
}
/** For debugging only */
@Override
public String toString() {
return includeKind.toString();
}
/**
* Compares styles according to their sorting order.
*/
@ -264,7 +269,6 @@ public class IncludeGroupStyle implements Comparable<IncludeGroupStyle> {
return includeKind.ordinal() - other.includeKind.ordinal();
}
public IncludeGroupStyle getGroupingStyle(Map<IncludeKind, IncludeGroupStyle> stylesMap) {
if (keepTogether)
return this;
@ -274,10 +278,25 @@ public class IncludeGroupStyle implements Comparable<IncludeGroupStyle> {
return stylesMap.get(IncludeKind.OTHER);
}
public IncludeGroupStyle getParentStyle(Map<IncludeKind, IncludeGroupStyle> stylesMap) {
private IncludeGroupStyle getParentStyle(Map<IncludeKind, IncludeGroupStyle> stylesMap) {
IncludeKind kind = includeKind.parent;
if (kind == null)
return null;
return stylesMap.get(kind);
}
public boolean isBlankLineNeededAfter(IncludeGroupStyle previousIncludeStyle,
Map<IncludeKind, IncludeGroupStyle> stylesMap) {
if (previousIncludeStyle == null)
return false;
IncludeGroupStyle groupingStyle = getGroupingStyle(stylesMap);
IncludeGroupStyle previousGroupingStyle = previousIncludeStyle.getGroupingStyle(stylesMap);
if (groupingStyle != previousGroupingStyle && groupingStyle.isBlankLineBefore())
return true;
IncludeGroupStyle parentStyle = groupingStyle.getParentStyle(stylesMap);
IncludeGroupStyle previousParentStyle = previousGroupingStyle.getParentStyle(stylesMap);
return parentStyle != null && previousParentStyle != null &&
parentStyle != previousParentStyle && parentStyle.isKeepTogether() &&
parentStyle.isBlankLineBefore();
}
}

View file

@ -84,6 +84,8 @@ import org.eclipse.cdt.utils.PathUtil;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap;
import org.eclipse.cdt.internal.core.dom.rewrite.util.ASTNodes;
import org.eclipse.cdt.internal.core.dom.rewrite.util.TextUtil;
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.IncludeGuardDetection;
@ -95,7 +97,8 @@ import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
* Organizes the include directives and forward declarations of a source or header file.
*/
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 final Collator COLLATOR = Collator.getInstance();
@ -227,19 +230,16 @@ public class IncludeOrganizer {
}
List<String> includeDirectives = new ArrayList<String>();
IncludeGroupStyle previousParentStyle = null;
IncludeGroupStyle previousStyle = null;
for (List<IncludePrototype> prototypes : groupedPrototypes) {
if (prototypes != null && !prototypes.isEmpty()) {
Collections.sort(prototypes);
Collections.sort(prototypes, preferences);
IncludeGroupStyle style = prototypes.get(0).getStyle();
IncludeGroupStyle groupingStyle = style.getGroupingStyle(preferences.includeStyles);
IncludeGroupStyle parentStyle = groupingStyle.getParentStyle(preferences.includeStyles);
boolean blankLineBefore = groupingStyle.isBlankLineBefore() ||
(parentStyle != null && parentStyle != previousParentStyle &&
parentStyle.isKeepTogether() && parentStyle.isBlankLineBefore());
previousParentStyle = parentStyle;
if (!includeDirectives.isEmpty() && blankLineBefore)
if (!includeDirectives.isEmpty() &&
style.isBlankLineNeededAfter(previousStyle, preferences.includeStyles)) {
includeDirectives.add(""); // Blank line separator //$NON-NLS-1$
}
previousStyle = style;
for (IncludePrototype prototype : prototypes) {
String trailingComment = ""; //$NON-NLS-1$
IASTPreprocessorIncludeStatement include = prototype.getExistingInclude();
@ -295,15 +295,15 @@ public class IncludeOrganizer {
int length = includeReplacementRegion.getLength();
if (allowReordering) {
if (buf.length() != 0) {
if (offset != 0 && !isPreviousLineBlank(offset))
if (offset != 0 && !TextUtil.isPreviousLineBlank(fContext.getSourceContents(), offset))
buf.insert(0, fLineDelimiter); // Blank line before.
if (!isBlankLineOrEndOfFile(offset + length))
buf.append(fLineDelimiter); // Blank line after.
}
String text = buf.toString();
// TODO(sprigogin): Add a diff algorithm and produce more narrow replacements.
if (!CharArrayUtils.equals(fContext.getTranslationUnit().getContents(), offset, length, text)) {
// TODO(sprigogin): Add a diff algorithm and produce narrower replacements.
if (!CharArrayUtils.equals(fContext.getSourceContents(), offset, length, text)) {
edits.add(new ReplaceEdit(offset, length, text));
}
} else if (buf.length() != 0) {
@ -470,7 +470,7 @@ public class IncludeOrganizer {
IASTFileLocation location = include.getFileLocation();
int offset = location.getNodeOffset();
if (fContext.getTranslationUnit().isCXXLanguage()) {
offset = getLineStart(fContext.getSourceContents(), offset);
offset = TextUtil.getLineStart(fContext.getSourceContents(), offset);
edits.add(new InsertEdit(offset, "//")); //$NON-NLS-1$
} else {
edits.add(new InsertEdit(offset, "/*")); //$NON-NLS-1$
@ -483,8 +483,8 @@ public class IncludeOrganizer {
IASTFileLocation location = include.getFileLocation();
int offset = location.getNodeOffset();
int endOffset = offset + location.getNodeLength();
offset = getLineStart(fContext.getSourceContents(), offset);
endOffset = skipToNextLine(fContext.getSourceContents(), endOffset);
offset = TextUtil.getLineStart(fContext.getSourceContents(), offset);
endOffset = TextUtil.skipToNextLine(fContext.getSourceContents(), endOffset);
edits.add(new DeleteEdit(offset, endOffset - offset));
}
@ -536,7 +536,7 @@ public class IncludeOrganizer {
}
if (includeOffset < 0) {
if (includeGuardEndOffset >= 0) {
includeOffset = skipToNextLine(contents, includeGuardEndOffset);
includeOffset = TextUtil.skipToNextLine(contents, includeGuardEndOffset);
} else {
includeOffset = 0;
}
@ -546,7 +546,7 @@ public class IncludeOrganizer {
}
includeEndOffset = includeOffset;
} else {
includeEndOffset = skipToNextLine(contents, includeEndOffset);
includeEndOffset = TextUtil.skipToNextLine(contents, includeEndOffset);
}
return new Region(includeOffset, includeEndOffset - includeOffset);
}
@ -587,32 +587,12 @@ public class IncludeOrganizer {
return CharArrayUtils.equals(((IASTPreprocessorPragmaStatement) statement).getMessage(), "once"); //$NON-NLS-1$
}
private static int skipToNextLine(char[] text, int offset) {
while (offset < text.length) {
if (text[offset++] == '\n')
break;
}
return offset;
}
private static int getLineStart(char[] text, int offset) {
while (--offset >= 0) {
if (text[offset] == '\n')
break;
}
return offset + 1;
}
private static int skipToNextLineAfterNode(char[] text, IASTNode node) {
return skipToNextLine(text, getNodeEndOffset(node));
}
/**
* Returns {@code true} if there are no non-whitespace characters between the given
* {@code offset} and the end of the line.
*/
private boolean isBlankLineOrEndOfFile(int offset) {
char[] contents = fContext.getTranslationUnit().getContents();
char[] contents = fContext.getSourceContents();
while (offset < contents.length) {
char c = contents[offset++];
if (c == '\n')
@ -623,26 +603,6 @@ public class IncludeOrganizer {
return true;
}
/**
* Returns {@code true} the line prior to the line corresponding to the given {@code offset}
* does not contain non-whitespace characters.
*/
private boolean isPreviousLineBlank(int offset) {
char[] contents = fContext.getTranslationUnit().getContents();
while (--offset >= 0) {
if (contents[offset] == '\n')
break;
}
while (--offset >= 0) {
char c = contents[offset];
if (c == '\n')
return true;
if (!Character.isWhitespace(c))
return false;
}
return false;
}
/**
* Returns the whitespace preceding the given node. The newline character in not considered
* whitespace for the purpose of this method.
@ -650,7 +610,7 @@ public class IncludeOrganizer {
private String getPrecedingWhitespace(IASTNode node) {
int offset = getNodeOffset(node);
if (offset >= 0) {
char[] contents = fContext.getTranslationUnit().getContents();
char[] contents = fContext.getSourceContents();
int i = offset;
while (--i >= 0) {
char c = contents[i];
@ -697,11 +657,11 @@ public class IncludeOrganizer {
for (int j = 1; j < leadingComments.size(); j++) {
comment = leadingComments.get(j);
if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1)
return skipToNextLineAfterNode(contents, previous);
return ASTNodes.skipToNextLineAfterNode(contents, previous);
previous = comment;
}
if (getStartingLineNumber(node) > getEndingLineNumber(previous) + 1)
return skipToNextLineAfterNode(contents, previous);
return ASTNodes.skipToNextLineAfterNode(contents, previous);
}
node = inverseFreestandingMap.get(comment);
if (node != null) {
@ -710,7 +670,7 @@ public class IncludeOrganizer {
for (int j = 1; j < freestandingComments.size(); j++) {
comment = freestandingComments.get(j);
if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1)
return skipToNextLineAfterNode(contents, previous);
return ASTNodes.skipToNextLineAfterNode(contents, previous);
previous = comment;
}
}
@ -1059,8 +1019,7 @@ public class IncludeOrganizer {
break;
}
}
buf.append("#include "); //$NON-NLS-1$
buf.append(include.getIncludeInfo().toString());
buf.append(include.getIncludeInfo().composeIncludeStatement());
buf.append(lineComment);
return buf.toString();
}

View file

@ -12,6 +12,7 @@ package org.eclipse.cdt.internal.ui.refactoring.includes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -21,12 +22,14 @@ import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.IncludeKind;
/**
* Preferences for managing of includes.
*/
public class IncludePreferences {
public class IncludePreferences implements Comparator<StyledInclude> {
private static final String DEFAULT_PARTNER_FILE_SUFFIXES = "test,unittest"; //$NON-NLS-1$
public static enum UnusedStatementDisposition { REMOVE, COMMENT_OUT, KEEP }
@ -111,7 +114,7 @@ public class IncludePreferences {
String value = PreferenceConstants.getPreference(preferenceKey, project, null);
IncludeGroupStyle style = null;
if (value != null)
style = IncludeGroupStyle.fromString(value, includeKind);
style = IncludeGroupStyle.fromXmlString(value, includeKind);
if (style == null)
style = new IncludeGroupStyle(includeKind);
includeStyles.put(includeKind, style);
@ -124,41 +127,41 @@ public class IncludePreferences {
*/
public static void initializeDefaultValues(IPreferenceStore store) {
IncludeGroupStyle style = new IncludeGroupStyle(IncludeKind.RELATED);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_RELATED, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_RELATED, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.PARTNER);
style.setKeepTogether(true);
style.setBlankLineBefore(true);
style.setOrder(0);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_PARTNER, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_PARTNER, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.IN_SAME_FOLDER);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SAME_FOLDER, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SAME_FOLDER, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.IN_SUBFOLDER);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SUBFOLDER, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SUBFOLDER, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.SYSTEM);
style.setKeepTogether(true);
style.setBlankLineBefore(true);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SYSTEM, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SYSTEM, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.SYSTEM_WITH_EXTENSION);
style.setKeepTogether(true);
style.setAngleBrackets(true);
style.setOrder(1);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITH_EXTENSION, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITH_EXTENSION, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.SYSTEM_WITHOUT_EXTENSION);
style.setKeepTogether(true);
style.setAngleBrackets(true);
style.setOrder(2);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.OTHER);
style.setKeepTogether(true);
style.setBlankLineBefore(true);
style.setOrder(3);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_OTHER, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_OTHER, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.IN_SAME_PROJECT);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SAME_PROJECT, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_SAME_PROJECT, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.IN_OTHER_PROJECT);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_OTHER_PROJECT, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_OTHER_PROJECT, style.toXmlString());
style = new IncludeGroupStyle(IncludeKind.EXTERNAL);
store.setDefault(PreferenceConstants.INCLUDE_STYLE_EXTERNAL, style.toString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_EXTERNAL, style.toXmlString());
store.setDefault(PreferenceConstants.INCLUDE_STYLE_MATCHING_PATTERN, ""); //$NON-NLS-1$
store.setDefault(PreferenceConstants.INCLUDES_PARTNER_FILE_SUFFIXES, DEFAULT_PARTNER_FILE_SUFFIXES);
@ -178,4 +181,13 @@ public class IncludePreferences {
store.setDefault(PreferenceConstants.INCLUDES_SYMBOL_EXPORTING_HEADERS,
SymbolExportMap.serializeMaps(Collections.singletonList(GCCHeaderSubstitutionMaps.getSymbolExportMap())));
}
@Override
public int compare(StyledInclude include1, StyledInclude include2) {
int c = include1.getStyle().getGroupingStyle(includeStyles).getOrder() -
include2.getStyle().getGroupingStyle(includeStyles).getOrder();
if (c != 0)
return c;
return include1.getIncludeInfo().compareTo(include2.getIncludeInfo());
}
}

View file

@ -13,6 +13,8 @@ package org.eclipse.cdt.internal.ui.refactoring.includes;
import org.eclipse.osgi.util.NLS;
public final class Messages extends NLS {
public static String AddInclude_help_provider_error;
public static String GCCHeaderSubstitutionMaps_c_map;
public static String GCCHeaderSubstitutionMaps_cpp_map;

View file

@ -9,5 +9,7 @@
# Sergey Prigogin (Google) - initial API and implementation
###############################################################################
AddInclude_help_provider_error=Help provider error
GCCHeaderSubstitutionMaps_c_map=GCC C Header Substitution
GCCHeaderSubstitutionMaps_cpp_map=GCC C++ Header Substitution

View file

@ -730,25 +730,18 @@ public class NewClassCodeGenerator {
includes.add(new StyledInclude(baseClassLocation, includeInfo, style));
}
}
Collections.sort(includes);
IncludePreferences preferences = inclusionContext.getPreferences();
Collections.sort(includes, preferences);
StringBuilder text = new StringBuilder();
IncludePreferences preferences = inclusionContext.getPreferences();
IncludeGroupStyle previousParentStyle = null;
IncludeGroupStyle previousStyle = null;
for (StyledInclude include : includes) {
IncludeGroupStyle style = include.getStyle();
IncludeGroupStyle groupingStyle = style.getGroupingStyle(preferences.includeStyles);
IncludeGroupStyle parentStyle = groupingStyle.getParentStyle(preferences.includeStyles);
if (groupingStyle.isBlankLineBefore() ||
(parentStyle != null && previousParentStyle != null &&
parentStyle != previousParentStyle && parentStyle.isKeepTogether() &&
parentStyle.isBlankLineBefore())) {
if (style.isBlankLineNeededAfter(previousStyle, preferences.includeStyles))
text.append(lineDelimiter);
}
text.append("#include "); //$NON-NLS-1$
text.append(include.getIncludeInfo().toString());
text.append(include.getIncludeInfo().composeIncludeStatement());
text.append(lineDelimiter);
previousParentStyle = parentStyle;
previousStyle = style;
}
monitor.done();
@ -756,7 +749,7 @@ public class NewClassCodeGenerator {
}
/**
* Checks if the base classes need to be verified (ie they must exist in the project)
* Checks if the base classes need to be verified (i.e. they must exist in the project)
*
* @return <code>true</code> if the base classes should be verified
*/

View file

@ -317,7 +317,7 @@ public class CUIPlugin extends AbstractUIPlugin {
}
public static void log(String message, Throwable e) {
log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR, message, e));
log(createErrorStatus(message, e));
}
public static void log(IStatus status) {
@ -339,9 +339,17 @@ public class CUIPlugin extends AbstractUIPlugin {
log(new Status(IStatus.ERROR, PLUGIN_ID, ICStatusConstants.INTERNAL_ERROR, message, null));
}
/**
* Creates an error status.
*
* @since 5.7
*/
public static Status createErrorStatus(String message, Throwable e) {
return new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR, message, e);
}
/**
* Utility method with conventions
* @param logError TODO
*/
public static void errorDialog(Shell shell, String title, String message, IStatus s, boolean logError) {
if (logError)
@ -357,7 +365,6 @@ public class CUIPlugin extends AbstractUIPlugin {
/**
* Utility method with conventions
* @param logError TODO
*/
public static void errorDialog(Shell shell, String title, String message, Throwable t, boolean logError) {
if (logError)

View file

@ -49,7 +49,7 @@ import org.eclipse.cdt.ui.refactoring.actions.RefactoringAction;
import org.eclipse.cdt.internal.ui.IContextMenuConstants;
import org.eclipse.cdt.internal.ui.actions.ActionMessages;
import org.eclipse.cdt.internal.ui.actions.CDTQuickMenuCreator;
import org.eclipse.cdt.internal.ui.editor.AddIncludeOnSelectionAction;
import org.eclipse.cdt.internal.ui.editor.AddIncludeAction;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.editor.ICEditorActionDefinitionIds;
import org.eclipse.cdt.internal.ui.editor.OrganizeIncludesAction;
@ -115,7 +115,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange
private List<ISelectionChangedListener> fRegisteredSelectionListeners;
private List<RefactoringAction> fRefactorActions= new ArrayList<RefactoringAction>();
private AddIncludeOnSelectionAction fAddInclude;
private AddIncludeAction fAddInclude;
// private OverrideMethodsAction fOverrideMethods;
// private GenerateHashCodeEqualsAction fHashCodeEquals;
private GettersAndSettersAction fAddGetterSetter;
@ -155,9 +155,9 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange
fEditor= editor;
fGroupName= groupName;
fAddInclude= new AddIncludeOnSelectionAction(editor);
fAddInclude= new AddIncludeAction(editor);
fAddInclude.setActionDefinitionId(ICEditorActionDefinitionIds.ADD_INCLUDE);
editor.setAction("AddIncludeOnSelection", fAddInclude); //$NON-NLS-1$
editor.setAction("AddInclude", fAddInclude); //$NON-NLS-1$
fOrganizeIncludes= new OrganizeIncludesAction(editor);
fOrganizeIncludes.setActionDefinitionId(ICEditorActionDefinitionIds.ORGANIZE_INCLUDES);