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:
parent
7ae8b48608
commit
ca5586f8be
31 changed files with 847 additions and 726 deletions
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#include "A.h"
|
||||
#include "OverloadedFunction.h"
|
||||
|
||||
#include "A.h"
|
||||
|
||||
using ns3::func;
|
||||
|
||||
void test() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "ResolvedName.h"
|
||||
#include "UnresolvedName.h"
|
||||
|
||||
#include "ResolvedName.h"
|
||||
|
||||
using ns4::A;
|
||||
using ns5::B;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "VariableTypeHelper.h"
|
||||
#include "VariableType.h"
|
||||
|
||||
#include "VariableTypeHelper.h"
|
||||
|
||||
namespace ns2 {
|
||||
|
||||
void VT::method() {
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Sergey Prigogin (Google) - initial API and implementation
|
||||
* Markus Schorn (Wind River Systems)
|
||||
* Sergey Prigogin (Google) - initial API and implementation
|
||||
* Markus Schorn (Wind River Systems)
|
||||
*******************************************************************************/
|
||||
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();
|
||||
|
|
|
@ -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$
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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$
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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,87 +132,274 @@ 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>();
|
||||
IIndexBinding adaptedBinding= index.adaptBinding(binding);
|
||||
if (adaptedBinding == null) {
|
||||
bindings.addAll(Arrays.asList(index.findBindings(nameChars, false, filter, new NullProgressMonitor())));
|
||||
} else {
|
||||
bindings.add(adaptedBinding);
|
||||
while (adaptedBinding instanceof ICPPSpecialization) {
|
||||
adaptedBinding= index.adaptBinding(((ICPPSpecialization) adaptedBinding).getSpecializedBinding());
|
||||
if (adaptedBinding != null) {
|
||||
bindings.add(adaptedBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IIndexBinding indexBinding : bindings) {
|
||||
// Replace ctor with the class itself.
|
||||
if (indexBinding instanceof ICPPConstructor) {
|
||||
indexBinding = indexBinding.getOwner();
|
||||
}
|
||||
IIndexName[] definitions= null;
|
||||
// class, struct, union, enum-type, enum-item
|
||||
if (indexBinding instanceof ICompositeType || indexBinding instanceof IEnumeration || indexBinding instanceof IEnumerator) {
|
||||
definitions= index.findDefinitions(indexBinding);
|
||||
} else if (indexBinding instanceof ITypedef || (indexBinding instanceof IFunction)) {
|
||||
definitions = index.findDeclarations(indexBinding);
|
||||
}
|
||||
if (definitions != null) {
|
||||
for (IIndexName definition : definitions) {
|
||||
considerForInclusion(definition, indexBinding, index, candidatesMap);
|
||||
}
|
||||
if (definitions.length > 0 && adaptedBinding != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
IIndexMacro[] macros = index.findMacros(nameChars, filter, new NullProgressMonitor());
|
||||
for (IIndexMacro macro : macros) {
|
||||
IIndexName definition = macro.getDefinition();
|
||||
considerForInclusion(definition, macro, index, candidatesMap);
|
||||
}
|
||||
|
||||
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) {
|
||||
candidates.clear();
|
||||
candidates.add((IncludeCandidate) dialog.getFirstResult());
|
||||
try {
|
||||
IIndex index = fContext.getIndex();
|
||||
IIndexBinding adaptedBinding= index.adaptBinding(binding);
|
||||
if (adaptedBinding == null) {
|
||||
bindings.addAll(Arrays.asList(index.findBindings(nameChars, false, filter, new NullProgressMonitor())));
|
||||
} else {
|
||||
bindings.add(adaptedBinding);
|
||||
while (adaptedBinding instanceof ICPPSpecialization) {
|
||||
adaptedBinding= index.adaptBinding(((ICPPSpecialization) adaptedBinding).getSpecializedBinding());
|
||||
if (adaptedBinding != null) {
|
||||
bindings.add(adaptedBinding);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (IIndexBinding indexBinding : bindings) {
|
||||
// Replace ctor with the class itself.
|
||||
if (indexBinding instanceof ICPPConstructor) {
|
||||
indexBinding = indexBinding.getOwner();
|
||||
}
|
||||
IIndexName[] definitions= null;
|
||||
// class, struct, union, enum-type, enum-item
|
||||
if (indexBinding instanceof ICompositeType || indexBinding instanceof IEnumeration || indexBinding instanceof IEnumerator) {
|
||||
definitions= index.findDefinitions(indexBinding);
|
||||
} else if (indexBinding instanceof ITypedef || (indexBinding instanceof IFunction)) {
|
||||
definitions = index.findDeclarations(indexBinding);
|
||||
}
|
||||
if (definitions != null) {
|
||||
for (IIndexName definition : definitions) {
|
||||
considerForInclusion(definition, indexBinding, index, candidatesMap);
|
||||
}
|
||||
if (definitions.length > 0 && adaptedBinding != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
IIndexMacro[] macros = index.findMacros(nameChars, filter, new NullProgressMonitor());
|
||||
for (IIndexMacro macro : macros) {
|
||||
IIndexName definition = macro.getDefinition();
|
||||
considerForInclusion(definition, macro, index, candidatesMap);
|
||||
}
|
||||
|
||||
final ArrayList<IncludeCandidate> candidates = new ArrayList<IncludeCandidate>(candidatesMap.values());
|
||||
if (candidates.size() > 1) {
|
||||
IncludeCandidate candidate = fAmbiguityResolver.selectElement(candidates);
|
||||
if (candidate == null)
|
||||
return Collections.emptyList();
|
||||
candidates.clear();
|
||||
candidates.add(candidate);
|
||||
}
|
||||
|
||||
if (candidates.size() == 1) {
|
||||
IncludeCandidate candidate = candidates.get(0);
|
||||
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.
|
||||
UsingDeclaration usingDeclaration = deduceUsingDeclaration(binding, indexBinding, ast);
|
||||
if (usingDeclaration != null)
|
||||
usingDeclarations.add(usingDeclaration);
|
||||
}
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
CUIPlugin.log(e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
fRequiredIncludes.clear();
|
||||
fUsingDeclarations.clear();
|
||||
if (candidates.size() == 1) {
|
||||
IncludeCandidate candidate = candidates.get(0);
|
||||
fRequiredIncludes.add(candidate.getInclude());
|
||||
IIndexBinding indexBinding = candidate.getBinding();
|
||||
|
||||
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);
|
||||
if (usingDeclaration != null)
|
||||
fUsingDeclarations.add(usingDeclaration);
|
||||
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$
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue