From ca5586f8be131c5a77cf896e094d0a75bd6fada1 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Tue, 13 Aug 2013 20:43:34 -0700 Subject: [PATCH] Bug 414624 - Add Include command doesn't honor Organize Includes preferences --- .../org.eclipse.cdt.core/META-INF/MANIFEST.MF | 2 +- .../changegenerator/ChangeGenerator.java | 74 +-- .../commenthandler/NodeCommentMap.java | 41 ++ .../core/dom/rewrite}/util/ASTNodes.java | 15 +- .../core/dom/rewrite/util/TextUtil.java | 73 +++ .../OverloadedFunction.cpp.expected | 3 +- .../addInclude/UnresolvedName.cpp.expected | 3 +- .../addInclude/VariableType.cpp.expected | 3 +- .../cdt/ui/tests/text/AddIncludeTest.java | 24 +- .../AddIncludesOperation.java | 272 --------- .../corext/codemanipulation/IncludeInfo.java | 4 + .../codemanipulation/StyledInclude.java | 7 +- .../code/flow/InputFlowAnalyzer.java | 2 +- .../internal/ui/editor/AddIncludeAction.java | 194 ++++++ .../ui/editor/CEditorActionContributor.java | 2 +- .../internal/ui/editor/CEditorMessages.java | 12 +- .../ui/editor/CEditorMessages.properties | 5 + .../ConstructedCEditorMessages.properties | 12 +- .../ui/preferences/IncludeStyleBlock.java | 4 +- .../ui/refactoring/NodeContainer.java | 2 +- .../includes/BindingClassifier.java | 32 +- .../includes/IElementSelector.java | 26 + .../includes/IncludeCreator.java} | 559 +++++++++++------- .../includes/IncludeGroupStyle.java | 29 +- .../includes/IncludeOrganizer.java | 89 +-- .../includes/IncludePreferences.java | 38 +- .../ui/refactoring/includes/Messages.java | 2 + .../refactoring/includes/Messages.properties | 2 + .../classwizard/NewClassCodeGenerator.java | 21 +- .../src/org/eclipse/cdt/ui/CUIPlugin.java | 13 +- .../cdt/ui/actions/GenerateActionGroup.java | 8 +- 31 files changed, 847 insertions(+), 726 deletions(-) rename core/{org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext => org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite}/util/ASTNodes.java (70%) create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/util/TextUtil.java delete mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/AddIncludesOperation.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeAction.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IElementSelector.java rename core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/{editor/AddIncludeOnSelectionAction.java => refactoring/includes/IncludeCreator.java} (51%) diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index 0badfaba81b..73f6b776b56 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -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, diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java index a2184ce8502..ce28e387a5c 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java @@ -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 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 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 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> getModifications(IASTNode node) { Map> modifications = classifiedModifications.get(node); if (modifications == null) diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommentMap.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommentMap.java index 829c2ddd4e2..74303f4def3 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommentMap.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommentMap.java @@ -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 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 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; + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/util/ASTNodes.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/util/ASTNodes.java similarity index 70% rename from core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/util/ASTNodes.java rename to core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/util/ASTNodes.java index 360ddfc0358..05b7e7aed52 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/util/ASTNodes.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/util/ASTNodes.java @@ -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)); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/util/TextUtil.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/util/TextUtil.java new file mode 100644 index 00000000000..2060abb34e5 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/util/TextUtil.java @@ -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; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/resources/addInclude/OverloadedFunction.cpp.expected b/core/org.eclipse.cdt.ui.tests/resources/addInclude/OverloadedFunction.cpp.expected index 1317a9d5181..4d350b67710 100644 --- a/core/org.eclipse.cdt.ui.tests/resources/addInclude/OverloadedFunction.cpp.expected +++ b/core/org.eclipse.cdt.ui.tests/resources/addInclude/OverloadedFunction.cpp.expected @@ -1,6 +1,7 @@ -#include "A.h" #include "OverloadedFunction.h" +#include "A.h" + using ns3::func; void test() { diff --git a/core/org.eclipse.cdt.ui.tests/resources/addInclude/UnresolvedName.cpp.expected b/core/org.eclipse.cdt.ui.tests/resources/addInclude/UnresolvedName.cpp.expected index 20cf3c1d8f7..47ca81e67a3 100644 --- a/core/org.eclipse.cdt.ui.tests/resources/addInclude/UnresolvedName.cpp.expected +++ b/core/org.eclipse.cdt.ui.tests/resources/addInclude/UnresolvedName.cpp.expected @@ -1,6 +1,7 @@ -#include "ResolvedName.h" #include "UnresolvedName.h" +#include "ResolvedName.h" + using ns4::A; using ns5::B; diff --git a/core/org.eclipse.cdt.ui.tests/resources/addInclude/VariableType.cpp.expected b/core/org.eclipse.cdt.ui.tests/resources/addInclude/VariableType.cpp.expected index d991b1486b7..5cb2db5bbec 100644 --- a/core/org.eclipse.cdt.ui.tests/resources/addInclude/VariableType.cpp.expected +++ b/core/org.eclipse.cdt.ui.tests/resources/addInclude/VariableType.cpp.expected @@ -1,6 +1,7 @@ -#include "VariableTypeHelper.h" #include "VariableType.h" +#include "VariableTypeHelper.h" + namespace ns2 { void VT::method() { diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AddIncludeTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AddIncludeTest.java index 6e10ccf182a..ed396126a5c 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AddIncludeTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AddIncludeTest.java @@ -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 selectElement(Collection 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(); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/AddIncludesOperation.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/AddIncludesOperation.java deleted file mode 100644 index fba5e650ca0..00000000000 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/AddIncludesOperation.java +++ /dev/null @@ -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 fIncludes; - private final List fUsings; - private String fNewLine; - private IBuffer fBuffer; - private List fExistingIncludes; - private List 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 includes, - List 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 toAdd = new ArrayList(); - 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 toAdd = new ArrayList(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 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 fBeforeOffset. - * 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$ - } -} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/IncludeInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/IncludeInfo.java index 7f7c3bbd58f..4468b68015e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/IncludeInfo.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/IncludeInfo.java @@ -89,6 +89,10 @@ public class IncludeInfo implements Comparable { return (isSystem ? '<' : '"') + name + (isSystem ? '>' : '"'); } + public String composeIncludeStatement() { + return "#include " + toString(); //$NON-NLS-1$ + } + @Override public int compareTo(IncludeInfo other) { if (isSystem != other.isSystem) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/StyledInclude.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/StyledInclude.java index f9012e1a9f6..3d444f7e340 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/StyledInclude.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/StyledInclude.java @@ -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 { +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 { this.existingInclude = existingInclude; } - @Override - public int compareTo(StyledInclude other) { - return includeInfo.compareTo(other.includeInfo); - } - @Override public int hashCode() { if (header != null) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java index cf9f29e7440..538954cbf00 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java @@ -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 { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeAction.java new file mode 100644 index 00000000000..376a6f91d61 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeAction.java @@ -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 selectElement(final Collection 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 edits = new ArrayList(); + 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; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java index 4894e4aa2c8..0503d97b018 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java @@ -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$ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java index c17f8e93625..c06c006c49f 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java @@ -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; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties index eeb64c72004..812812b454f 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties @@ -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 diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties index bd462477db4..72da918416c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties @@ -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 diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStyleBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStyleBlock.java index e47842b5ddd..49907239ae1 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStyleBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStyleBlock.java @@ -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); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NodeContainer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NodeContainer.java index 00f9ae4e6f9..8018d70810b 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NodeContainer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NodeContainer.java @@ -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 nodes; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java index 2fb578b487b..340ebd874cf 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java @@ -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. * diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IElementSelector.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IElementSelector.java new file mode 100644 index 00000000000..350b484ad4e --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IElementSelector.java @@ -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 selectElement(Collection elements); +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeOnSelectionAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreator.java similarity index 51% rename from core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeOnSelectionAction.java rename to core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreator.java index 90f87d79e13..cc167c73bf0 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeOnSelectionAction.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreator.java @@ -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 fRequiredIncludes = new ArrayList(); - private final List fUsingDeclarations = new ArrayList(); - 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 includes, List 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 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 candidatesMap= new HashMap(); final IndexFilter filter = IndexFilter.getDeclaredBindingFilter(ast.getLinkage().getLinkageID(), false); + final List requiredIncludes = new ArrayList(); + final List usingDeclarations = new ArrayList(); + List bindings = new ArrayList(); - 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 candidates = new ArrayList(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 candidates = new ArrayList(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 createEdits(List includes, + List 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 edits = new ArrayList(); + + IASTPreprocessorIncludeStatement[] existingIncludes = ast.getIncludeDirectives(); + fContext.addHeadersIncludedPreviously(existingIncludes); + + List styledIncludes = new ArrayList(); + // 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 mergedIncludes = new ArrayList(); + 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 mergedUsingDeclarations = getUsingDeclarations(ast); + for (UsingDeclaration usingDeclaration : mergedUsingDeclarations) { + for (Iterator iter = usingDeclarations.iterator(); iter.hasNext();) { + UsingDeclaration using = iter.next(); + if (using.equals(usingDeclaration.name)) + iter.remove(); + } + } + + if (usingDeclarations.isEmpty()) + return edits; + + List temp = null; + for (Iterator 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(); + 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 getUsingDeclarations(IASTTranslationUnit ast) { + List usingDeclarations = new ArrayList(); + 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 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 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 true 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 { + 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$ + } + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeGroupStyle.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeGroupStyle.java index 6b9c4802eef..a02d50ea65c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeGroupStyle.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeGroupStyle.java @@ -227,8 +227,7 @@ public class IncludeGroupStyle implements Comparable { 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 { 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 { 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 { return includeKind.ordinal() - other.includeKind.ordinal(); } - public IncludeGroupStyle getGroupingStyle(Map stylesMap) { if (keepTogether) return this; @@ -274,10 +278,25 @@ public class IncludeGroupStyle implements Comparable { return stylesMap.get(IncludeKind.OTHER); } - public IncludeGroupStyle getParentStyle(Map stylesMap) { + private IncludeGroupStyle getParentStyle(Map stylesMap) { IncludeKind kind = includeKind.parent; if (kind == null) return null; return stylesMap.get(kind); } + + public boolean isBlankLineNeededAfter(IncludeGroupStyle previousIncludeStyle, + Map 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(); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java index aa7612c6330..2441ddc457c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java @@ -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 includeDirectives = new ArrayList(); - IncludeGroupStyle previousParentStyle = null; + IncludeGroupStyle previousStyle = null; for (List 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(); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java index 2daf0bde035..fa2ce41bf5c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java @@ -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 { 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()); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java index ce0348dbfe6..8a235ac0d59 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java @@ -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; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties index e4c80e304cd..7ef265474ab 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties @@ -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 diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java index e39bb79720e..047647fff9c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java @@ -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 true if the base classes should be verified */ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java index 3915d9cbc8f..42954fe9b3e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java @@ -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) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java index 16333a03b4b..241d3bce7a6 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java @@ -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 fRegisteredSelectionListeners; private List fRefactorActions= new ArrayList(); - 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);