mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
Automatic update of include statements and include guards when header
files are renamed or moved.
This commit is contained in:
parent
cf4556f05f
commit
d962e2eee7
30 changed files with 1580 additions and 379 deletions
|
@ -2,7 +2,7 @@ Manifest-Version: 1.0
|
|||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: %pluginName
|
||||
Bundle-SymbolicName: org.eclipse.cdt.core; singleton:=true
|
||||
Bundle-Version: 5.7.0.qualifier
|
||||
Bundle-Version: 5.8.0.qualifier
|
||||
Bundle-Activator: org.eclipse.cdt.core.CCorePlugin
|
||||
Bundle-Vendor: %providerName
|
||||
Bundle-Localization: plugin
|
||||
|
|
|
@ -77,7 +77,8 @@ public class WorkingCopy extends TranslationUnit implements IWorkingCopy {
|
|||
CommitWorkingCopyOperation op= new CommitWorkingCopyOperation(this, force);
|
||||
op.runOperation(monitor);
|
||||
} else {
|
||||
String contents = this.getSource();
|
||||
IBuffer buffer = getBuffer();
|
||||
String contents = buffer.getContents();
|
||||
if (contents == null)
|
||||
return;
|
||||
|
||||
|
|
|
@ -502,54 +502,6 @@ public abstract class ASTTranslationUnit extends ASTNode implements IASTTranslat
|
|||
return fSizeofCalculator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset of the given node, or -1 if the node is not part of the translation
|
||||
* unit file or doesn't have a file-location.
|
||||
* @see IASTNode#getFileLocation()
|
||||
*/
|
||||
public static int getNodeOffset(IASTNode node) {
|
||||
if (!node.isPartOfTranslationUnitFile())
|
||||
return -1;
|
||||
IASTFileLocation nodeLocation = node.getFileLocation();
|
||||
return nodeLocation != null ? nodeLocation.getNodeOffset() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end offset of the given node, or -1 if the node is not part of the translation
|
||||
* unit file or doesn't have a file-location.
|
||||
* @see IASTNode#getFileLocation()
|
||||
*/
|
||||
public static int getNodeEndOffset(IASTNode node) {
|
||||
if (!node.isPartOfTranslationUnitFile())
|
||||
return -1;
|
||||
IASTFileLocation nodeLocation = node.getFileLocation();
|
||||
return nodeLocation != null ? nodeLocation.getNodeOffset() + nodeLocation.getNodeLength() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 1-based starting line number of the given node, or 0 if the node is not part of
|
||||
* the translation unit file or doesn't have a file-location.
|
||||
* @see IASTNode#getFileLocation()
|
||||
*/
|
||||
public static int getStartingLineNumber(IASTNode node) {
|
||||
if (!node.isPartOfTranslationUnitFile())
|
||||
return 0;
|
||||
IASTFileLocation nodeLocation = node.getFileLocation();
|
||||
return nodeLocation != null ? nodeLocation.getStartingLineNumber() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 1-based ending line number of the given node, or 0 if the node is not part of
|
||||
* the translation unit file or doesn't have a file-location.
|
||||
* @see IASTNode#getFileLocation()
|
||||
*/
|
||||
public static int getEndingLineNumber(IASTNode node) {
|
||||
if (!node.isPartOfTranslationUnitFile())
|
||||
return 0;
|
||||
IASTFileLocation nodeLocation = node.getFileLocation();
|
||||
return nodeLocation != null ? nodeLocation.getEndingLineNumber() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNodesOmitted() {
|
||||
return fNodesOmitted;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012 Google, Inc and others.
|
||||
* Copyright (c) 2012, 2014 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
|
||||
|
@ -10,8 +10,6 @@
|
|||
*******************************************************************************/
|
||||
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;
|
||||
|
||||
|
@ -24,18 +22,51 @@ public class ASTNodes {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the offset of an AST node.
|
||||
* Returns the offset of the given node, or -1 if the node is not part of the translation
|
||||
* unit file or doesn't have a file-location.
|
||||
* @see IASTNode#getFileLocation()
|
||||
*/
|
||||
public static int offset(IASTNode node) {
|
||||
return node.getFileLocation().getNodeOffset();
|
||||
if (!node.isPartOfTranslationUnitFile())
|
||||
return -1;
|
||||
IASTFileLocation nodeLocation = node.getFileLocation();
|
||||
return nodeLocation != null ? nodeLocation.getNodeOffset() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exclusive end offset of an AST node.
|
||||
* Returns the end offset of the given node, or -1 if the node is not part of the translation
|
||||
* unit file or doesn't have a file-location.
|
||||
* @see IASTNode#getFileLocation()
|
||||
*/
|
||||
public static int endOffset(IASTNode node) {
|
||||
IASTFileLocation location = node.getFileLocation();
|
||||
return location.getNodeOffset() + location.getNodeLength();
|
||||
if (!node.isPartOfTranslationUnitFile())
|
||||
return -1;
|
||||
IASTFileLocation nodeLocation = node.getFileLocation();
|
||||
return nodeLocation != null ? nodeLocation.getNodeOffset() + nodeLocation.getNodeLength() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 1-based starting line number of the given node, or 0 if the node is not part of
|
||||
* the translation unit file or doesn't have a file-location.
|
||||
* @see IASTNode#getFileLocation()
|
||||
*/
|
||||
public static int getStartingLineNumber(IASTNode node) {
|
||||
if (!node.isPartOfTranslationUnitFile())
|
||||
return 0;
|
||||
IASTFileLocation nodeLocation = node.getFileLocation();
|
||||
return nodeLocation != null ? nodeLocation.getStartingLineNumber() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 1-based ending line number of the given node, or 0 if the node is not part of
|
||||
* the translation unit file or doesn't have a file-location.
|
||||
* @see IASTNode#getFileLocation()
|
||||
*/
|
||||
public static int getEndingLineNumber(IASTNode node) {
|
||||
if (!node.isPartOfTranslationUnitFile())
|
||||
return 0;
|
||||
IASTFileLocation nodeLocation = node.getFileLocation();
|
||||
return nodeLocation != null ? nodeLocation.getEndingLineNumber() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,6 +74,25 @@ public class ASTNodes {
|
|||
* offset if there is no line delimiter after the node.
|
||||
*/
|
||||
public static int skipToNextLineAfterNode(String text, IASTNode node) {
|
||||
return TextUtil.skipToNextLine(text, getNodeEndOffset(node));
|
||||
return TextUtil.skipToNextLine(text, endOffset(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the whitespace preceding the given node. The newline character in not considered
|
||||
* whitespace for the purpose of this method.
|
||||
*/
|
||||
public static String getPrecedingWhitespaceInLine(String text, IASTNode node) {
|
||||
int offset = offset(node);
|
||||
if (offset >= 0) {
|
||||
int i = offset;
|
||||
while (--i >= 0) {
|
||||
char c = text.charAt(i);
|
||||
if (c == '\n' || !Character.isWhitespace(c))
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
return text.substring(i, offset);
|
||||
}
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,17 @@ public class TextUtil {
|
|||
return offset + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset of the beginning of the line before the one containing the given offset,
|
||||
* or the beginning of the line containing the given offset if it is first in the text.
|
||||
*/
|
||||
public static int getPreviousLineStart(String text, int offset) {
|
||||
offset = getLineStart(text, offset);
|
||||
if (offset != 0)
|
||||
offset = getLineStart(text, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the line corresponding to the given {@code offset} does not contain
|
||||
* non-whitespace characters.
|
||||
|
@ -80,4 +91,23 @@ public class TextUtil {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the beginning offset of the first blank line contained between the two given offsets.
|
||||
* Returns -1, if not found.
|
||||
*/
|
||||
public static int findBlankLine(String text, int startOffset, int endOffset) {
|
||||
int blankOffset = startOffset == 0 || text.charAt(startOffset - 1) == '\n' ? startOffset : -1;
|
||||
while (startOffset < endOffset) {
|
||||
char c = text.charAt(startOffset++);
|
||||
if (c == '\n') {
|
||||
if (blankOffset >= 0)
|
||||
return blankOffset;
|
||||
blankOffset = startOffset;
|
||||
} else if (!Character.isWhitespace(c)) {
|
||||
blankOffset = -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<version>5.7.0-SNAPSHOT</version>
|
||||
<version>5.8.0-SNAPSHOT</version>
|
||||
<artifactId>org.eclipse.cdt.core</artifactId>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012, 2013 Google, Inc and others.
|
||||
* Copyright (c) 2012, 2014 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
|
||||
|
@ -17,9 +17,11 @@ import java.io.StringReader;
|
|||
import java.net.URI;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.core.filesystem.URIUtil;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
|
@ -42,6 +44,7 @@ import org.eclipse.cdt.core.model.CoreModel;
|
|||
import org.eclipse.cdt.core.model.ICProject;
|
||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||
import org.eclipse.cdt.core.testplugin.CProjectHelper;
|
||||
import org.eclipse.cdt.core.testplugin.TestScannerProvider;
|
||||
import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
|
||||
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
|
||||
import org.eclipse.cdt.ui.CUIPlugin;
|
||||
|
@ -55,6 +58,7 @@ import org.eclipse.cdt.internal.ui.refactoring.CRefactoringContext;
|
|||
* Common base for refactoring tests.
|
||||
*/
|
||||
public abstract class RefactoringTestBase extends BaseTestCase {
|
||||
private static final Pattern FILENAME_PATTERN = Pattern.compile("^(\\w+/)*\\w+\\.\\w+$");
|
||||
/** Allows empty files to be created during test setup. */
|
||||
protected boolean createEmptyFiles = true;
|
||||
/** See {@link PreferenceConstants.CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER} */
|
||||
|
@ -67,7 +71,7 @@ public abstract class RefactoringTestBase extends BaseTestCase {
|
|||
|
||||
private boolean cpp = true;
|
||||
private ICProject cproject;
|
||||
private final Set<TestSourceFile> testFiles = new LinkedHashSet<TestSourceFile>();
|
||||
private final Set<TestSourceFile> testFiles = new LinkedHashSet<>();
|
||||
private TestSourceFile selectedFile;
|
||||
private TextSelection selection;
|
||||
private TestSourceFile historyScript;
|
||||
|
@ -87,22 +91,41 @@ public abstract class RefactoringTestBase extends BaseTestCase {
|
|||
cproject = cpp ?
|
||||
CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) :
|
||||
CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER);
|
||||
TestScannerProvider.sLocalIncludes = new String[] { cproject.getProject().getLocation().toOSString() };
|
||||
|
||||
Bundle bundle = CTestPlugin.getDefault().getBundle();
|
||||
CharSequence[] testData = TestSourceReader.getContentsForTest(bundle, "ui", getClass(), getName(), 0);
|
||||
|
||||
for (CharSequence contents : testData) {
|
||||
TestSourceFile testFile = null;
|
||||
boolean firstAfterDelimiter = false;
|
||||
boolean expectedResult = false;
|
||||
BufferedReader reader = new BufferedReader(new StringReader(contents.toString()));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
String trimmedLine = line.trim();
|
||||
if (testFile == null) {
|
||||
assertTrue("Invalid file name \"" + trimmedLine + "\"", trimmedLine.matches("^(\\w+/)*\\w+\\.\\w+$"));
|
||||
testFile = new TestSourceFile(trimmedLine);
|
||||
if (isResultDelimiter(trimmedLine)) {
|
||||
expectedResult = true;
|
||||
firstAfterDelimiter = true;
|
||||
testFile = new TestSourceFile(null);
|
||||
} else {
|
||||
assertTrue("Invalid file name \"" + trimmedLine + "\"",
|
||||
FILENAME_PATTERN.matcher(trimmedLine).matches());
|
||||
testFile = new TestSourceFile(trimmedLine);
|
||||
}
|
||||
} else if (isResultDelimiter(trimmedLine)) {
|
||||
expectedResult = true;
|
||||
firstAfterDelimiter = true;
|
||||
} else if (expectedResult) {
|
||||
if (firstAfterDelimiter) {
|
||||
firstAfterDelimiter = false;
|
||||
if (FILENAME_PATTERN.matcher(trimmedLine).matches()) {
|
||||
testFile.setExpectedName(trimmedLine);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
assertTrue(testFile.getExpectedName() != null);
|
||||
testFile.addLineToExpectedSource(line);
|
||||
} else {
|
||||
testFile.addLineToSource(line);
|
||||
|
@ -110,7 +133,7 @@ public abstract class RefactoringTestBase extends BaseTestCase {
|
|||
}
|
||||
reader.close();
|
||||
|
||||
if (createEmptyFiles || !testFile.getSource().isEmpty()) {
|
||||
if (testFile.getName() != null && (createEmptyFiles || !testFile.getSource().isEmpty())) {
|
||||
TestSourceReader.createFile(cproject.getProject(), new Path(testFile.getName()),
|
||||
testFile.getSource());
|
||||
}
|
||||
|
@ -149,7 +172,7 @@ public abstract class RefactoringTestBase extends BaseTestCase {
|
|||
executeRefactoring(false);
|
||||
}
|
||||
|
||||
private void executeRefactoring(boolean expectedSuccess) throws Exception {
|
||||
protected void executeRefactoring(boolean expectedSuccess) throws Exception {
|
||||
if (ascendingVisibilityOrder) {
|
||||
getPreferenceStore().setValue(PreferenceConstants.CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER,
|
||||
ascendingVisibilityOrder);
|
||||
|
@ -169,6 +192,11 @@ public abstract class RefactoringTestBase extends BaseTestCase {
|
|||
executeRefactoring(refactoring, context, true, expectedSuccess);
|
||||
}
|
||||
|
||||
protected void executeRefactoring(Refactoring refactoring, boolean expectedSuccess)
|
||||
throws CoreException, Exception {
|
||||
executeRefactoring(refactoring, null, false, expectedSuccess);
|
||||
}
|
||||
|
||||
protected void executeRefactoring(Refactoring refactoring, RefactoringContext context,
|
||||
boolean withUserInput, boolean expectedSuccess) throws CoreException, Exception {
|
||||
try {
|
||||
|
@ -232,6 +260,10 @@ public abstract class RefactoringTestBase extends BaseTestCase {
|
|||
return cproject;
|
||||
}
|
||||
|
||||
protected IProject getProject() {
|
||||
return cproject.getProject();
|
||||
}
|
||||
|
||||
protected TestSourceFile getSelectedTestFile() {
|
||||
return selectedFile;
|
||||
}
|
||||
|
@ -348,7 +380,7 @@ public abstract class RefactoringTestBase extends BaseTestCase {
|
|||
protected void compareFiles() throws Exception {
|
||||
for (TestSourceFile testFile : testFiles) {
|
||||
String expectedSource = testFile.getExpectedSource();
|
||||
IFile file = cproject.getProject().getFile(new Path(testFile.getName()));
|
||||
IFile file = cproject.getProject().getFile(new Path(testFile.getExpectedName()));
|
||||
String actualSource = getFileContents(file);
|
||||
expectedSource= expectedSource.replace("\r\n", "\n");
|
||||
actualSource= actualSource.replace("\r\n", "\n");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2008, 2012 Institute for Software, HSR Hochschule fuer Technik
|
||||
* Copyright (c) 2008, 2014 Institute for Software, HSR Hochschule fuer Technik
|
||||
* Rapperswil, University of applied sciences and others
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
@ -12,6 +12,7 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.ui.tests.refactoring;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -27,6 +28,7 @@ public class TestSourceFile {
|
|||
private static final Pattern SELECTION_END = Pattern.compile("/\\*\\$\\$\\*/"); //$NON-NLS-1$
|
||||
|
||||
private final String name;
|
||||
private String expectedName;
|
||||
private final StringBuilder source = new StringBuilder();
|
||||
private final StringBuilder expectedSource = new StringBuilder();
|
||||
private int selectionStart = -1;
|
||||
|
@ -45,6 +47,20 @@ public class TestSourceFile {
|
|||
return source.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected name after refactoring.
|
||||
*/
|
||||
public String getExpectedName() {
|
||||
return expectedName == null ? name : expectedName;
|
||||
}
|
||||
|
||||
public void setExpectedName(String name) {
|
||||
expectedName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected contents after refactoring.
|
||||
*/
|
||||
public String getExpectedSource() {
|
||||
if (expectedSource.length() == 0) {
|
||||
return getSource();
|
||||
|
@ -80,7 +96,7 @@ public class TestSourceFile {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
return Objects.hash(name, expectedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,6 +106,6 @@ public class TestSourceFile {
|
|||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
TestSourceFile other = (TestSourceFile) obj;
|
||||
return name.equals(other.name);
|
||||
return Objects.equals(name, other.name) && Objects.equals(expectedName, other.expectedName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public class IncludeOrganizerTest extends IncludesTestBase {
|
|||
*/
|
||||
private String organizeIncludes(ITranslationUnit tu) throws Exception {
|
||||
IHeaderChooser headerChooser = new FirstHeaderChooser();
|
||||
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, LINE_DELIMITER, headerChooser);
|
||||
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, headerChooser);
|
||||
MultiTextEdit edit = organizer.organizeIncludes(ast);
|
||||
IDocument document = new Document(new String(tu.getContents()));
|
||||
edit.apply(document);
|
||||
|
@ -597,4 +597,4 @@ public class IncludeOrganizerTest extends IncludesTestBase {
|
|||
preferenceStore.setValue(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, true);
|
||||
assertExpectedResults();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public class RenameMacroTests extends RenameTestBase {
|
|||
IFile cpp= importFile("test.cpp", contents); //$NON-NLS-1$
|
||||
|
||||
int offset1= contents.indexOf("HALLO"); //$NON-NLS-1$
|
||||
int offset2= contents.indexOf("HALLO", offset1+1); //$NON-NLS-1$
|
||||
int offset2= contents.indexOf("HALLO", offset1 + 1); //$NON-NLS-1$
|
||||
|
||||
Change ch= getRefactorChanges(cpp, offset1, "WELT"); //$NON-NLS-1$
|
||||
assertTotalChanges(6, ch);
|
||||
|
@ -120,7 +120,6 @@ public class RenameMacroTests extends RenameTestBase {
|
|||
importFile("other.cpp", buf.toString()); //$NON-NLS-1$
|
||||
waitForIndexer();
|
||||
|
||||
|
||||
int offset1= contents.indexOf("MACRO"); //$NON-NLS-1$
|
||||
|
||||
// conflicts after renaming
|
||||
|
@ -130,7 +129,7 @@ public class RenameMacroTests extends RenameTestBase {
|
|||
"New element: w1 \n" +
|
||||
"Conflicting element type: Local variable"); //$NON-NLS-1$
|
||||
status= checkConditions(cpp, contents.indexOf("par1"), "MACRO"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
assertRefactoringError(status, "'MACRO' conflicts with the name of an existing macro!"); //$NON-NLS-1$
|
||||
assertRefactoringError(status, "'MACRO' conflicts with the name of an existing macro."); //$NON-NLS-1$
|
||||
status= checkConditions(cpp, offset1, "par1"); //$NON-NLS-1$
|
||||
assertRefactoringError(status, "A conflict was encountered during refactoring. \n" +
|
||||
"Type of problem: Name conflict \n" +
|
||||
|
@ -201,7 +200,7 @@ public class RenameMacroTests extends RenameTestBase {
|
|||
IFile cpp= importFile("test.cpp", contents); //$NON-NLS-1$
|
||||
|
||||
int offset1= contents.indexOf("_guard"); //$NON-NLS-1$
|
||||
int offset2= contents.indexOf("_guard", offset1+1); //$NON-NLS-1$
|
||||
int offset2= contents.indexOf("_guard", offset1 + 1); //$NON-NLS-1$
|
||||
Change ch= getRefactorChanges(cpp, offset2, "WELT"); //$NON-NLS-1$
|
||||
assertTotalChanges(2, 0, 1, ch);
|
||||
int off= offset1;
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 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.ui.tests.refactoring.rename;
|
||||
|
||||
import junit.framework.Test;
|
||||
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.ltk.core.refactoring.participants.MoveRefactoring;
|
||||
import org.eclipse.ltk.core.refactoring.participants.RenameRefactoring;
|
||||
import org.eclipse.ltk.internal.core.refactoring.resource.MoveResourcesProcessor;
|
||||
import org.eclipse.ltk.internal.core.refactoring.resource.RenameResourceProcessor;
|
||||
|
||||
import org.eclipse.cdt.ui.PreferenceConstants;
|
||||
import org.eclipse.cdt.ui.tests.refactoring.RefactoringTestBase;
|
||||
|
||||
import org.eclipse.cdt.internal.ui.refactoring.CRefactoring;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link org.eclipse.cdt.internal.ui.refactoring.rename.HeaderFileRenameParticipant} and
|
||||
* {@link org.eclipse.cdt.internal.ui.refactoring.rename.HeaderFileMoveParticipant}.
|
||||
*/
|
||||
public class RenameMoveHeaderRefactoringTest extends RefactoringTestBase {
|
||||
|
||||
public RenameMoveHeaderRefactoringTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RenameMoveHeaderRefactoringTest(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return suite(RenameMoveHeaderRefactoringTest.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetPreferences() {
|
||||
super.resetPreferences();
|
||||
getPreferenceStore().setToDefault(PreferenceConstants.FUNCTION_OUTPUT_PARAMETERS_BEFORE_INPUT);
|
||||
getPreferenceStore().setToDefault(PreferenceConstants.FUNCTION_PASS_OUTPUT_PARAMETERS_BY_POINTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CRefactoring createRefactoring() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// test1.h
|
||||
//#ifndef TEST1_H_
|
||||
//#define TEST1_H_
|
||||
//
|
||||
//class A {};
|
||||
//
|
||||
//#endif // TEST1_H_
|
||||
//====================
|
||||
// test.h
|
||||
//#ifndef TEST_H_
|
||||
//#define TEST_H_
|
||||
//
|
||||
//class A {};
|
||||
//
|
||||
//#endif // TEST_H_
|
||||
|
||||
// test.cpp
|
||||
//#include <string>
|
||||
//#include "test1.h" /* Comment1 */ // Comment2
|
||||
//====================
|
||||
// test.cpp
|
||||
//#include "test.h" /* Comment1 */ // Comment2
|
||||
//
|
||||
//#include <string>
|
||||
public void testFileRename() throws Exception {
|
||||
IResource resource = getProject().getFile("test1.h");
|
||||
RenameResourceProcessor processor = new RenameResourceProcessor(resource);
|
||||
processor.setNewResourceName("test.h");
|
||||
RenameRefactoring refactoring = new RenameRefactoring(processor);
|
||||
executeRefactoring(refactoring, true);
|
||||
compareFiles();
|
||||
}
|
||||
|
||||
// header1.h
|
||||
//#ifndef HEADER1_H_
|
||||
//#define HEADER1_H_
|
||||
//
|
||||
//class A {};
|
||||
//
|
||||
//#endif // HEADER1_H_
|
||||
//====================
|
||||
// dir/header1.h
|
||||
//#ifndef DIR_HEADER1_H_
|
||||
//#define DIR_HEADER1_H_
|
||||
//
|
||||
//class A {};
|
||||
//
|
||||
//#endif // DIR_HEADER1_H_
|
||||
|
||||
// source1.cpp
|
||||
//#include "header1.h"
|
||||
//====================
|
||||
// source1.cpp
|
||||
//#include "dir/header1.h"
|
||||
public void testFileMove() throws Exception {
|
||||
IResource resource = getProject().getFile("header1.h");
|
||||
MoveResourcesProcessor processor = new MoveResourcesProcessor(new IResource[] { resource });
|
||||
IFolder destination = getProject().getFolder("dir");
|
||||
destination.create(true, true, npm());
|
||||
processor.setDestination(destination);
|
||||
MoveRefactoring refactoring = new MoveRefactoring(processor);
|
||||
executeRefactoring(refactoring, true);
|
||||
compareFiles();
|
||||
}
|
||||
|
||||
// dir1/header1.h
|
||||
//#ifndef DIR1_HEADER1_H_
|
||||
//#define DIR1_HEADER1_H_
|
||||
//
|
||||
//#include "dir1/header2.h"
|
||||
//
|
||||
//#endif // DIR1_HEADER1_H_
|
||||
//====================
|
||||
// dir3/header1.h
|
||||
//#ifndef DIR3_HEADER1_H_
|
||||
//#define DIR3_HEADER1_H_
|
||||
//
|
||||
//#include "dir3/header2.h"
|
||||
//
|
||||
//#endif // DIR3_HEADER1_H_
|
||||
|
||||
// dir1/header2.h
|
||||
//#if !defined(DIR1_HEADER2_H_)
|
||||
//#define DIR1_HEADER2_H_
|
||||
//
|
||||
//class A {};
|
||||
//
|
||||
//#endif /* DIR1_HEADER2_H_ */
|
||||
//====================
|
||||
// dir3/header2.h
|
||||
//#if !defined(DIR3_HEADER2_H_)
|
||||
//#define DIR3_HEADER2_H_
|
||||
//
|
||||
//class A {};
|
||||
//
|
||||
//#endif /* DIR3_HEADER2_H_ */
|
||||
|
||||
// dir1/source1.cpp
|
||||
//#include <string>
|
||||
//
|
||||
//#include "dir1/header1.h"
|
||||
//====================
|
||||
// dir3/source1.cpp
|
||||
//#include <string>
|
||||
//
|
||||
//#include "dir3/header1.h"
|
||||
|
||||
// header2.cpp
|
||||
//#include "dir1/header1.h"
|
||||
//#include "dir2/header3.h"
|
||||
//
|
||||
//#ifdef SOMETHING
|
||||
// #include "dir1/header2.h"
|
||||
//#endif
|
||||
//====================
|
||||
//#include "dir2/header3.h"
|
||||
//#include "dir3/header1.h"
|
||||
//
|
||||
//#ifdef SOMETHING
|
||||
// #include "dir3/header2.h"
|
||||
//#endif
|
||||
public void testFolderRename() throws Exception {
|
||||
IFolder resource = getProject().getFolder("dir1");
|
||||
RenameResourceProcessor processor = new RenameResourceProcessor(resource);
|
||||
processor.setNewResourceName("dir3");
|
||||
RenameRefactoring refactoring = new RenameRefactoring(processor);
|
||||
executeRefactoring(refactoring, true);
|
||||
compareFiles();
|
||||
}
|
||||
|
||||
// dir1/header1.h
|
||||
//#ifndef DIR1_HEADER1_H_
|
||||
//#define DIR1_HEADER1_H_
|
||||
//
|
||||
//#include "dir1/header2.h"
|
||||
//
|
||||
//#endif // DIR1_HEADER1_H_
|
||||
//====================
|
||||
// dir3/dir1/header1.h
|
||||
//#ifndef DIR3_DIR1_HEADER1_H_
|
||||
//#define DIR3_DIR1_HEADER1_H_
|
||||
//
|
||||
//#include "dir3/dir1/header2.h"
|
||||
//
|
||||
//#endif // DIR3_DIR1_HEADER1_H_
|
||||
|
||||
// dir1/header2.h
|
||||
//#if !defined(DIR1_HEADER2_H_)
|
||||
//#define DIR1_HEADER2_H_
|
||||
//
|
||||
//class A {};
|
||||
//
|
||||
//#endif /* DIR1_HEADER2_H_ */
|
||||
//====================
|
||||
// dir3/dir1/header2.h
|
||||
//#if !defined(DIR3_DIR1_HEADER2_H_)
|
||||
//#define DIR3_DIR1_HEADER2_H_
|
||||
//
|
||||
//class A {};
|
||||
//
|
||||
//#endif /* DIR3_DIR1_HEADER2_H_ */
|
||||
|
||||
// dir1/source1.cpp
|
||||
//#include <string>
|
||||
//
|
||||
//#include "dir1/header1.h"
|
||||
//====================
|
||||
// dir3/dir1/source1.cpp
|
||||
//#include <string>
|
||||
//
|
||||
//#include "dir3/dir1/header1.h"
|
||||
|
||||
// header2.cpp
|
||||
//#include "dir1/header1.h"
|
||||
//#include "dir2/header3.h"
|
||||
//
|
||||
//int x = 0;
|
||||
//#include "dir1/header2.h"
|
||||
//====================
|
||||
//#include "dir2/header3.h"
|
||||
//#include "dir3/dir1/header1.h"
|
||||
//
|
||||
//int x = 0;
|
||||
//#include "dir3/dir1/header2.h"
|
||||
public void testFolderMove() throws Exception {
|
||||
IFolder resource = getProject().getFolder("dir1");
|
||||
MoveResourcesProcessor processor = new MoveResourcesProcessor(new IResource[] { resource });
|
||||
IFolder destination = getProject().getFolder("dir3");
|
||||
destination.create(true, true, npm());
|
||||
processor.setDestination(destination);
|
||||
MoveRefactoring refactoring = new MoveRefactoring(processor);
|
||||
executeRefactoring(refactoring, true);
|
||||
compareFiles();
|
||||
}
|
||||
}
|
|
@ -604,7 +604,9 @@ preferenceKeywords.smarttyping=editor typing type close comment tabs indentation
|
|||
historyAction.label = History...
|
||||
createScriptAction.label = Create Script...
|
||||
applyScriptAction.label = Apply Script...
|
||||
renameParticipant.name = Source Folder Rename
|
||||
renameFolderParticipant.name = Source Folder Rename
|
||||
headerFileMoveParticipant.name = Header File Move
|
||||
headerFileRenameParticipant.name = Header File Rename
|
||||
|
||||
FormatAction.label= &Format
|
||||
IndentAction.label= Correct &Indentation
|
||||
|
|
|
@ -4396,23 +4396,55 @@
|
|||
id="org.eclipse.cdt.internal.ui.refactoring.extractfunction.ExtractFunctionRefactoring">
|
||||
</contribution>
|
||||
</extension>
|
||||
<extension point="org.eclipse.ltk.core.refactoring.renameParticipants">
|
||||
<renameParticipant id="org.eclipse.cdt.ui.RenameSourceFolder"
|
||||
name="%renameParticipant.name"
|
||||
class="org.eclipse.cdt.internal.ui.refactoring.rename.RenameSourceFolder">
|
||||
<extension point="org.eclipse.ltk.core.refactoring.moveParticipants">
|
||||
<moveParticipant id="org.eclipse.cdt.ui.headerFileMoveParticipant"
|
||||
name="%headerFileMoveParticipant.name"
|
||||
class="org.eclipse.cdt.internal.ui.refactoring.rename.HeaderFileMoveParticipant">
|
||||
<enablement>
|
||||
<or>
|
||||
<with variable="affectedNatures">
|
||||
<iterate operator="or">
|
||||
<equals value="org.eclipse.cdt.core.ccnature" />
|
||||
</iterate>
|
||||
</with>
|
||||
<with variable="affectedNatures">
|
||||
<iterate operator="or">
|
||||
<with variable="affectedNatures">
|
||||
<iterate operator="or">
|
||||
<or>
|
||||
<equals value="org.eclipse.cdt.core.cnature" />
|
||||
</iterate>
|
||||
</with>
|
||||
</or>
|
||||
<equals value="org.eclipse.cdt.core.ccnature" />
|
||||
</or>
|
||||
</iterate>
|
||||
</with>
|
||||
<with variable="element">
|
||||
<instanceof value="org.eclipse.core.resources.IResource"/>
|
||||
</with>
|
||||
</enablement>
|
||||
</moveParticipant>
|
||||
</extension>
|
||||
<extension point="org.eclipse.ltk.core.refactoring.renameParticipants">
|
||||
<renameParticipant id="org.eclipse.cdt.ui.headerFileRenameParticipant"
|
||||
name="%headerFileRenameParticipant.name"
|
||||
class="org.eclipse.cdt.internal.ui.refactoring.rename.HeaderFileRenameParticipant">
|
||||
<enablement>
|
||||
<with variable="affectedNatures">
|
||||
<iterate operator="or">
|
||||
<or>
|
||||
<equals value="org.eclipse.cdt.core.cnature" />
|
||||
<equals value="org.eclipse.cdt.core.ccnature" />
|
||||
</or>
|
||||
</iterate>
|
||||
</with>
|
||||
<with variable="element">
|
||||
<instanceof value="org.eclipse.core.resources.IResource"/>
|
||||
</with>
|
||||
</enablement>
|
||||
</renameParticipant>
|
||||
<renameParticipant id="org.eclipse.cdt.ui.RenameSourceFolder"
|
||||
name="%renameFolderParticipant.name"
|
||||
class="org.eclipse.cdt.internal.ui.refactoring.rename.SourceFolderRenameParticipant">
|
||||
<enablement>
|
||||
<with variable="affectedNatures">
|
||||
<iterate operator="or">
|
||||
<or>
|
||||
<equals value="org.eclipse.cdt.core.cnature" />
|
||||
<equals value="org.eclipse.cdt.core.ccnature" />
|
||||
</or>
|
||||
</iterate>
|
||||
</with>
|
||||
<with variable="element">
|
||||
<instanceof value="org.eclipse.core.resources.IFolder" />
|
||||
</with>
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.eclipse.core.resources.IProject;
|
|||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
|
||||
import org.eclipse.cdt.core.model.CModelException;
|
||||
import org.eclipse.cdt.core.model.ICProject;
|
||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||
import org.eclipse.cdt.core.parser.IScannerInfo;
|
||||
|
@ -44,6 +45,8 @@ public class InclusionContext {
|
|||
private final Map<IncludeInfo, IPath> fIncludeResolutionCache;
|
||||
private final Map<IPath, IncludeInfo> fInverseIncludeResolutionCache;
|
||||
private final IncludePreferences fPreferences;
|
||||
private String fSourceContents;
|
||||
private String fLineDelimiter;
|
||||
|
||||
public InclusionContext(ITranslationUnit tu) {
|
||||
fTu = tu;
|
||||
|
@ -141,7 +144,41 @@ public class InclusionContext {
|
|||
return include;
|
||||
}
|
||||
|
||||
public IncludeGroupStyle getIncludeStyle(IPath headerPath) {
|
||||
/**
|
||||
* Returns the include directive that resolves to the given header file, or {@code null} if
|
||||
* the file is not on the include search path. Current directory is not considered to be a part
|
||||
* of the include path by this method.
|
||||
*/
|
||||
public IncludeInfo getIncludeForHeaderFile(IPath fullPath, boolean isSystem) {
|
||||
IncludeInfo include = fInverseIncludeResolutionCache.get(fullPath);
|
||||
if (include != null)
|
||||
return include;
|
||||
String headerLocation = fullPath.toOSString();
|
||||
String shortestInclude = null;
|
||||
for (IncludeSearchPathElement pathElement : fIncludeSearchPath.getElements()) {
|
||||
if (isSystem && pathElement.isForQuoteIncludesOnly())
|
||||
continue;
|
||||
String includeDirective = pathElement.getIncludeDirective(headerLocation);
|
||||
if (includeDirective != null &&
|
||||
(shortestInclude == null || shortestInclude.length() > includeDirective.length())) {
|
||||
shortestInclude = includeDirective;
|
||||
}
|
||||
}
|
||||
if (shortestInclude == null) {
|
||||
if (fIncludeSearchPath.isInhibitUseOfCurrentFileDirectory() ||
|
||||
!fCurrentDirectory.isPrefixOf(fullPath)) {
|
||||
return null;
|
||||
}
|
||||
shortestInclude = fullPath.setDevice(null).removeFirstSegments(fCurrentDirectory.segmentCount()).toString();
|
||||
}
|
||||
include = new IncludeInfo(shortestInclude, isSystem);
|
||||
// Don't put an include to fullPath to fIncludeResolutionCache since it may be wrong
|
||||
// if the header was included by #include_next.
|
||||
fInverseIncludeResolutionCache.put(fullPath, include);
|
||||
return include;
|
||||
}
|
||||
|
||||
public IncludeGroupStyle getIncludeStyle(IPath headerPath) {
|
||||
IncludeKind includeKind;
|
||||
IncludeInfo includeInfo = getIncludeForHeaderFile(headerPath);
|
||||
if (includeInfo != null && includeInfo.isSystem()) {
|
||||
|
@ -252,4 +289,22 @@ public class InclusionContext {
|
|||
return null;
|
||||
return relativePath.toString();
|
||||
}
|
||||
|
||||
public String getSourceContents() {
|
||||
if (fSourceContents == null) {
|
||||
fSourceContents = new String(fTu.getContents());
|
||||
}
|
||||
return fSourceContents;
|
||||
}
|
||||
|
||||
public String getLineDelimiter() {
|
||||
if (fLineDelimiter == null) {
|
||||
try {
|
||||
fLineDelimiter = StubUtility.getLineDelimiterUsed(fTu);
|
||||
} catch (CModelException e) {
|
||||
fLineDelimiter = System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$
|
||||
}
|
||||
}
|
||||
return fLineDelimiter;
|
||||
}
|
||||
}
|
|
@ -666,7 +666,7 @@ public class StubUtility {
|
|||
return projectStore.findTemplateById(id);
|
||||
}
|
||||
|
||||
private static String generateIncludeGuardSymbol(IResource file, ICProject cproject) {
|
||||
public static String generateIncludeGuardSymbol(IResource file, ICProject cproject) {
|
||||
int scheme = PreferenceConstants.getPreference(
|
||||
PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME, cproject,
|
||||
PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_NAME);
|
||||
|
|
|
@ -92,6 +92,6 @@ public class StyledInclude {
|
|||
/** For debugging only */
|
||||
@Override
|
||||
public String toString() {
|
||||
return header != null ? header.toPortableString() : includeInfo.toString();
|
||||
return header != null ? header.toString() : includeInfo.toString();
|
||||
}
|
||||
}
|
|
@ -99,7 +99,6 @@ public class AddIncludeAction extends TextEditorAction {
|
|||
return;
|
||||
}
|
||||
|
||||
final String lineDelimiter = getLineDelimiter(editor);
|
||||
final MultiTextEdit[] holder = new MultiTextEdit[1];
|
||||
SharedASTJob job = new SharedASTJob(CEditorMessages.AddInclude_action, tu) {
|
||||
@Override
|
||||
|
@ -113,7 +112,7 @@ public class AddIncludeAction extends TextEditorAction {
|
|||
IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT);
|
||||
try {
|
||||
index.acquireReadLock();
|
||||
IncludeCreator creator = new IncludeCreator(tu, index, lineDelimiter, fAmbiguityResolver);
|
||||
IncludeCreator creator = new IncludeCreator(tu, index, fAmbiguityResolver);
|
||||
holder[0] = creator.createInclude(ast, (ITextSelection) selection);
|
||||
return Status.OK_STATUS;
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -154,19 +153,6 @@ public class AddIncludeAction extends TextEditorAction {
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -66,7 +66,6 @@ public class OrganizeIncludesAction extends TextEditorAction {
|
|||
|
||||
final IHeaderChooser headerChooser = new InteractiveHeaderChooser(
|
||||
CEditorMessages.OrganizeIncludes_label, editor.getSite().getShell());
|
||||
final String lineDelimiter = getLineDelimiter(editor);
|
||||
final MultiTextEdit[] holder = new MultiTextEdit[1];
|
||||
SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) {
|
||||
@Override
|
||||
|
@ -80,7 +79,7 @@ public class OrganizeIncludesAction extends TextEditorAction {
|
|||
IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT);
|
||||
try {
|
||||
index.acquireReadLock();
|
||||
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, lineDelimiter, headerChooser);
|
||||
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, headerChooser);
|
||||
holder[0] = organizer.organizeIncludes(ast);
|
||||
return Status.OK_STATUS;
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -115,19 +114,6 @@ public class OrganizeIncludesAction extends TextEditorAction {
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -37,7 +37,6 @@ public class IncludeCreationContext extends InclusionContext {
|
|||
private final Set<IPath> fHeadersToInclude;
|
||||
private final Set<IPath> fHeadersAlreadyIncluded;
|
||||
private final Set<IPath> fHeadersIncludedPreviously;
|
||||
private String fSourceContents;
|
||||
|
||||
public IncludeCreationContext(ITranslationUnit tu, IIndex index) {
|
||||
super(tu);
|
||||
|
@ -47,13 +46,6 @@ public class IncludeCreationContext extends InclusionContext {
|
|||
fHeadersIncludedPreviously = new HashSet<>();
|
||||
}
|
||||
|
||||
public String getSourceContents() {
|
||||
if (fSourceContents == null) {
|
||||
fSourceContents = new String(getTranslationUnit().getContents());
|
||||
}
|
||||
return fSourceContents;
|
||||
}
|
||||
|
||||
public final IIndex getIndex() {
|
||||
return fIndex;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
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.net.URI;
|
||||
import java.util.ArrayDeque;
|
||||
|
@ -32,7 +31,6 @@ import org.eclipse.core.resources.IProject;
|
|||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.content.IContentType;
|
||||
import org.eclipse.jface.text.IRegion;
|
||||
import org.eclipse.jface.text.ITextSelection;
|
||||
|
@ -98,13 +96,10 @@ import org.eclipse.cdt.internal.ui.CHelpProviderManager;
|
|||
public class IncludeCreator {
|
||||
private static final Collator COLLATOR = Collator.getInstance();
|
||||
|
||||
private final String fLineDelimiter;
|
||||
private final IElementSelector fAmbiguityResolver;
|
||||
private final IncludeCreationContext fContext;
|
||||
|
||||
public IncludeCreator(ITranslationUnit tu, IIndex index, String lineDelimiter,
|
||||
IElementSelector ambiguityResolver) {
|
||||
fLineDelimiter = lineDelimiter;
|
||||
public IncludeCreator(ITranslationUnit tu, IIndex index, IElementSelector ambiguityResolver) {
|
||||
fAmbiguityResolver = ambiguityResolver;
|
||||
fContext = new IncludeCreationContext(tu, index);
|
||||
}
|
||||
|
@ -237,7 +232,7 @@ public class IncludeCreator {
|
|||
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
|
||||
String contents = fContext.getSourceContents();
|
||||
IRegion includeRegion =
|
||||
IncludeOrganizer.getSafeIncludeReplacementRegion(contents, ast, commentedNodeMap);
|
||||
IncludeUtil.getSafeIncludeReplacementRegion(contents, ast, commentedNodeMap);
|
||||
|
||||
IncludePreferences preferences = fContext.getPreferences();
|
||||
|
||||
|
@ -258,21 +253,8 @@ public class IncludeCreator {
|
|||
}
|
||||
Collections.sort(styledIncludes, preferences);
|
||||
|
||||
// Populate list of existing includes in the include insertion region.
|
||||
List<StyledInclude> 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);
|
||||
}
|
||||
}
|
||||
List<StyledInclude> mergedIncludes =
|
||||
IncludeUtil.getIncludesInRegion(existingIncludes, includeRegion, fContext);
|
||||
|
||||
if (preferences.allowReordering) {
|
||||
// Since the order of existing include statements may not match the include order
|
||||
|
@ -299,23 +281,23 @@ public class IncludeCreator {
|
|||
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
|
||||
flushEditBuffer(offset, text, rootEdit);
|
||||
if (contents.charAt(offset - 1) != '\n')
|
||||
text.append(fLineDelimiter);
|
||||
text.append(fContext.getLineDelimiter());
|
||||
}
|
||||
if (include.getStyle().isBlankLineNeededAfter(previousInclude.getStyle(), preferences.includeStyles)) {
|
||||
if (TextUtil.isLineBlank(contents, offset)) {
|
||||
offset = TextUtil.skipToNextLine(contents, offset);
|
||||
} else {
|
||||
text.append(fLineDelimiter);
|
||||
text.append(fContext.getLineDelimiter());
|
||||
}
|
||||
}
|
||||
}
|
||||
text.append(include.getIncludeInfo().composeIncludeStatement());
|
||||
text.append(fLineDelimiter);
|
||||
text.append(fContext.getLineDelimiter());
|
||||
} else {
|
||||
if (previousInclude != null && previousInclude.getExistingInclude() == null &&
|
||||
include.getStyle().isBlankLineNeededAfter(previousInclude.getStyle(), preferences.includeStyles) &&
|
||||
!TextUtil.isPreviousLineBlank(contents, ASTNodes.offset(existingInclude))) {
|
||||
text.append(fLineDelimiter);
|
||||
text.append(fContext.getLineDelimiter());
|
||||
}
|
||||
flushEditBuffer(offset, text, rootEdit);
|
||||
}
|
||||
|
@ -323,7 +305,7 @@ public class IncludeCreator {
|
|||
}
|
||||
if (includeRegion.getLength() == 0 && !TextUtil.isLineBlank(contents, includeRegion.getOffset()) &&
|
||||
!includes.isEmpty()) {
|
||||
text.append(fLineDelimiter);
|
||||
text.append(fContext.getLineDelimiter());
|
||||
}
|
||||
flushEditBuffer(offset, text, rootEdit);
|
||||
|
||||
|
@ -359,7 +341,7 @@ public class IncludeCreator {
|
|||
|
||||
if (mergedUsingDeclarations.isEmpty()) {
|
||||
offset = includeRegion.getOffset() + includeRegion.getLength();
|
||||
text.append(fLineDelimiter); // Blank line between includes and using declarations.
|
||||
text.append(fContext.getLineDelimiter()); // Blank line between includes and using declarations.
|
||||
} else {
|
||||
offset = commentedNodeMap.getOffsetIncludingComments(mergedUsingDeclarations.get(0).existingDeclaration);
|
||||
}
|
||||
|
@ -382,11 +364,11 @@ public class IncludeCreator {
|
|||
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
|
||||
flushEditBuffer(offset, text, rootEdit);
|
||||
if (contents.charAt(offset - 1) != '\n')
|
||||
text.append(fLineDelimiter);
|
||||
text.append(fContext.getLineDelimiter());
|
||||
}
|
||||
}
|
||||
text.append(using.composeDirective());
|
||||
text.append(fLineDelimiter);
|
||||
text.append(fContext.getLineDelimiter());
|
||||
} else {
|
||||
flushEditBuffer(offset, text, rootEdit);
|
||||
}
|
||||
|
|
|
@ -12,10 +12,6 @@
|
|||
package org.eclipse.cdt.internal.ui.refactoring.includes;
|
||||
|
||||
import static org.eclipse.cdt.core.index.IndexLocationFactory.getAbsolutePath;
|
||||
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getEndingLineNumber;
|
||||
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeEndOffset;
|
||||
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeOffset;
|
||||
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getStartingLineNumber;
|
||||
import static org.eclipse.cdt.internal.ui.refactoring.includes.IncludeUtil.isContainedInRegion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -34,7 +30,6 @@ import org.eclipse.core.runtime.OperationCanceledException;
|
|||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.jface.text.IRegion;
|
||||
import org.eclipse.jface.text.Region;
|
||||
import org.eclipse.text.edits.DeleteEdit;
|
||||
import org.eclipse.text.edits.InsertEdit;
|
||||
import org.eclipse.text.edits.MultiTextEdit;
|
||||
|
@ -47,13 +42,9 @@ import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
|
|||
import org.eclipse.cdt.core.dom.ast.DOMException;
|
||||
import org.eclipse.cdt.core.dom.ast.EScopeKind;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTComment;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorPragmaStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||
import org.eclipse.cdt.core.dom.ast.IBinding;
|
||||
import org.eclipse.cdt.core.dom.ast.ICompositeType;
|
||||
|
@ -77,10 +68,7 @@ import org.eclipse.cdt.core.index.IIndexFileSet;
|
|||
import org.eclipse.cdt.core.index.IIndexInclude;
|
||||
import org.eclipse.cdt.core.index.IIndexName;
|
||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||
import org.eclipse.cdt.core.parser.Keywords;
|
||||
import org.eclipse.cdt.core.parser.util.ArrayUtil;
|
||||
import org.eclipse.cdt.core.parser.util.CharArrayIntMap;
|
||||
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
|
||||
import org.eclipse.cdt.ui.CUIPlugin;
|
||||
import org.eclipse.cdt.ui.CodeGeneration;
|
||||
|
||||
|
@ -88,10 +76,7 @@ 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;
|
||||
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
|
||||
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
|
||||
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
|
||||
import org.eclipse.cdt.internal.formatter.ChangeFormatter;
|
||||
|
@ -182,11 +167,8 @@ public class IncludeOrganizer {
|
|||
|
||||
private final IHeaderChooser fHeaderChooser;
|
||||
private final IncludeCreationContext fContext;
|
||||
private final String fLineDelimiter;
|
||||
|
||||
public IncludeOrganizer(ITranslationUnit tu, IIndex index, String lineDelimiter,
|
||||
IHeaderChooser headerChooser) {
|
||||
fLineDelimiter = lineDelimiter;
|
||||
public IncludeOrganizer(ITranslationUnit tu, IIndex index, IHeaderChooser headerChooser) {
|
||||
fHeaderChooser = headerChooser;
|
||||
fContext = new IncludeCreationContext(tu, index);
|
||||
}
|
||||
|
@ -240,7 +222,7 @@ public class IncludeOrganizer {
|
|||
|
||||
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
|
||||
IRegion includeReplacementRegion =
|
||||
getSafeIncludeReplacementRegion(fContext.getSourceContents(), ast, commentedNodeMap);
|
||||
IncludeUtil.getSafeIncludeReplacementRegion(fContext.getSourceContents(), ast, commentedNodeMap);
|
||||
|
||||
IncludePreferences preferences = fContext.getPreferences();
|
||||
boolean allowReordering = preferences.allowReordering || existingIncludes.length == 0;
|
||||
|
@ -300,7 +282,7 @@ public class IncludeOrganizer {
|
|||
List<IASTComment> comments = commentedNodeMap.getTrailingCommentsForNode(include);
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (IASTComment comment : comments) {
|
||||
buf.append(getPrecedingWhitespace(comment));
|
||||
buf.append(ASTNodes.getPrecedingWhitespaceInLine(fContext.getSourceContents(), comment));
|
||||
buf.append(comment.getRawSignature());
|
||||
}
|
||||
trailingComment = buf.toString();
|
||||
|
@ -318,7 +300,7 @@ public class IncludeOrganizer {
|
|||
StringBuilder buf = new StringBuilder();
|
||||
for (String include : includeDirectives) {
|
||||
buf.append(include);
|
||||
buf.append(fLineDelimiter);
|
||||
buf.append(fContext.getLineDelimiter());
|
||||
}
|
||||
|
||||
int offset = includeReplacementRegion.getOffset();
|
||||
|
@ -326,7 +308,7 @@ public class IncludeOrganizer {
|
|||
if (allowReordering) {
|
||||
if (buf.length() != 0) {
|
||||
if (offset != 0 && !TextUtil.isPreviousLineBlank(fContext.getSourceContents(), offset))
|
||||
buf.insert(0, fLineDelimiter); // Blank line before.
|
||||
buf.insert(0, fContext.getLineDelimiter()); // Blank line before.
|
||||
}
|
||||
|
||||
String text = buf.toString();
|
||||
|
@ -500,7 +482,7 @@ public class IncludeOrganizer {
|
|||
|
||||
for (ForwardDeclarationNode node : typeDeclarationsRoot.children) {
|
||||
if (pendingBlankLine) {
|
||||
buf.append(fLineDelimiter);
|
||||
buf.append(fContext.getLineDelimiter());
|
||||
pendingBlankLine = false;
|
||||
}
|
||||
printNode(node, buf);
|
||||
|
@ -508,14 +490,14 @@ public class IncludeOrganizer {
|
|||
|
||||
for (ForwardDeclarationNode node : nonTypeDeclarationsRoot.children) {
|
||||
if (pendingBlankLine) {
|
||||
buf.append(fLineDelimiter);
|
||||
buf.append(fContext.getLineDelimiter());
|
||||
pendingBlankLine = false;
|
||||
}
|
||||
printNode(node, buf);
|
||||
}
|
||||
|
||||
if ((pendingBlankLine || buf.length() != 0) && !isBlankLineOrEndOfFile(offset))
|
||||
buf.append(fLineDelimiter);
|
||||
buf.append(fContext.getLineDelimiter());
|
||||
|
||||
if (buf.length() != 0)
|
||||
rootEdit.addChild(new InsertEdit(offset, buf.toString()));
|
||||
|
@ -523,15 +505,15 @@ public class IncludeOrganizer {
|
|||
|
||||
private void printNode(ForwardDeclarationNode node, StringBuilder buf) throws CoreException {
|
||||
if (node.declaration == null) {
|
||||
buf.append(CodeGeneration.getNamespaceBeginContent(fContext.getTranslationUnit(), node.name, fLineDelimiter));
|
||||
buf.append(CodeGeneration.getNamespaceBeginContent(fContext.getTranslationUnit(), node.name, fContext.getLineDelimiter()));
|
||||
for (ForwardDeclarationNode child : node.children) {
|
||||
printNode(child, buf);
|
||||
}
|
||||
buf.append(CodeGeneration.getNamespaceEndContent(fContext.getTranslationUnit(), node.name, fLineDelimiter));
|
||||
buf.append(CodeGeneration.getNamespaceEndContent(fContext.getTranslationUnit(), node.name, fContext.getLineDelimiter()));
|
||||
} else {
|
||||
buf.append(node.declaration);
|
||||
}
|
||||
buf.append(fLineDelimiter);
|
||||
buf.append(fContext.getLineDelimiter());
|
||||
}
|
||||
|
||||
private void createCommentOut(IASTPreprocessorIncludeStatement include, MultiTextEdit rootEdit) {
|
||||
|
@ -566,95 +548,6 @@ public class IncludeOrganizer {
|
|||
}
|
||||
}
|
||||
|
||||
static IRegion getSafeIncludeReplacementRegion(String contents, IASTTranslationUnit ast,
|
||||
NodeCommentMap commentMap) {
|
||||
int maxSafeOffset = ast.getFileLocation().getNodeLength();
|
||||
IASTDeclaration[] declarations = ast.getDeclarations(true);
|
||||
if (declarations.length != 0)
|
||||
maxSafeOffset = declarations[0].getFileLocation().getNodeOffset();
|
||||
|
||||
boolean topCommentSkipped = false;
|
||||
int includeOffset = -1;
|
||||
int includeEndOffset = -1;
|
||||
int includeGuardStatementsToSkip = getNumberOfIncludeGuardStatementsToSkip(ast);
|
||||
int includeGuardEndOffset = -1;
|
||||
for (IASTPreprocessorStatement statement : ast.getAllPreprocessorStatements()) {
|
||||
if (statement.isPartOfTranslationUnitFile()) {
|
||||
IASTFileLocation fileLocation = statement.getFileLocation();
|
||||
int offset = fileLocation.getNodeOffset();
|
||||
if (offset >= maxSafeOffset)
|
||||
break;
|
||||
int endOffset = offset + fileLocation.getNodeLength();
|
||||
|
||||
if (includeGuardStatementsToSkip > 0) {
|
||||
--includeGuardStatementsToSkip;
|
||||
includeGuardEndOffset = endOffset;
|
||||
if (!commentMap.getLeadingCommentsForNode(statement).isEmpty()) {
|
||||
topCommentSkipped = true;
|
||||
}
|
||||
} else if (statement instanceof IASTPreprocessorIncludeStatement) {
|
||||
if (includeOffset < 0)
|
||||
includeOffset = offset;
|
||||
includeEndOffset = endOffset;
|
||||
includeGuardStatementsToSkip = 0; // Just in case
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (includeOffset < 0) {
|
||||
if (includeGuardEndOffset >= 0) {
|
||||
includeOffset = TextUtil.skipToNextLine(contents, includeGuardEndOffset);
|
||||
} else {
|
||||
includeOffset = 0;
|
||||
}
|
||||
if (!topCommentSkipped) {
|
||||
// Skip the first comment block near the top of the file.
|
||||
includeOffset = skipStandaloneCommentBlock(contents, includeOffset, maxSafeOffset, ast.getComments(), commentMap);
|
||||
}
|
||||
includeEndOffset = includeOffset;
|
||||
} else {
|
||||
includeEndOffset = TextUtil.skipToNextLine(contents, includeEndOffset);
|
||||
}
|
||||
return new Region(includeOffset, includeEndOffset - includeOffset);
|
||||
}
|
||||
|
||||
private static int getNumberOfIncludeGuardStatementsToSkip(IASTTranslationUnit ast) {
|
||||
IASTPreprocessorStatement statement = findFirstPreprocessorStatement(ast);
|
||||
if (statement == null)
|
||||
return 0;
|
||||
|
||||
int num = 0;
|
||||
int offset = 0;
|
||||
if (isPragmaOnce(statement)) {
|
||||
num++;
|
||||
offset = getNodeEndOffset(statement);
|
||||
}
|
||||
char[] contents = ast.getRawSignature().toCharArray();
|
||||
if (offset != 0)
|
||||
contents = Arrays.copyOfRange(contents, offset, contents.length);
|
||||
CharArrayIntMap ppKeywords= new CharArrayIntMap(40, -1);
|
||||
Keywords.addKeywordsPreprocessor(ppKeywords);
|
||||
if (IncludeGuardDetection.detectIncludeGuard(new CharArray(contents), new LexerOptions(), ppKeywords) != null) {
|
||||
num += 2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
private static IASTPreprocessorStatement findFirstPreprocessorStatement(IASTTranslationUnit ast) {
|
||||
for (IASTPreprocessorStatement statement : ast.getAllPreprocessorStatements()) {
|
||||
if (statement.isPartOfTranslationUnitFile())
|
||||
return statement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isPragmaOnce(IASTPreprocessorStatement statement) {
|
||||
if (!(statement instanceof IASTPreprocessorPragmaStatement))
|
||||
return false;
|
||||
return CharArrayUtils.equals(((IASTPreprocessorPragmaStatement) statement).getMessage(), "once"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if there are no non-whitespace characters between the given
|
||||
* {@code offset} and the end of the line.
|
||||
|
@ -671,82 +564,6 @@ public class IncludeOrganizer {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the whitespace preceding the given node. The newline character in not considered
|
||||
* whitespace for the purpose of this method.
|
||||
*/
|
||||
private String getPrecedingWhitespace(IASTNode node) {
|
||||
int offset = getNodeOffset(node);
|
||||
if (offset >= 0) {
|
||||
String contents = fContext.getSourceContents();
|
||||
int i = offset;
|
||||
while (--i >= 0) {
|
||||
char c = contents.charAt(i);
|
||||
if (c == '\n' || !Character.isWhitespace(c))
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
return contents.substring(i, offset);
|
||||
}
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
private static int skipStandaloneCommentBlock(String contents, int offset, int endOffset,
|
||||
IASTComment[] comments, NodeCommentMap commentMap) {
|
||||
Map<IASTComment, IASTNode> inverseLeadingMap = new HashMap<>();
|
||||
for (Map.Entry<IASTNode, List<IASTComment>> entry : commentMap.getLeadingMap().entrySet()) {
|
||||
IASTNode node = entry.getKey();
|
||||
if (getNodeOffset(node) <= endOffset) {
|
||||
for (IASTComment comment : entry.getValue()) {
|
||||
inverseLeadingMap.put(comment, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
Map<IASTComment, IASTNode> inverseFreestandingMap = new HashMap<>();
|
||||
for (Map.Entry<IASTNode, List<IASTComment>> entry : commentMap.getFreestandingMap().entrySet()) {
|
||||
IASTNode node = entry.getKey();
|
||||
if (getNodeEndOffset(node) < endOffset) {
|
||||
for (IASTComment comment : entry.getValue()) {
|
||||
inverseFreestandingMap.put(comment, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < comments.length; i++) {
|
||||
IASTComment comment = comments[i];
|
||||
int commentOffset = getNodeOffset(comment);
|
||||
if (commentOffset >= offset) {
|
||||
if (commentOffset >= endOffset)
|
||||
break;
|
||||
IASTNode node = inverseLeadingMap.get(comment);
|
||||
if (node != null) {
|
||||
List<IASTComment> leadingComments = commentMap.getLeadingMap().get(node);
|
||||
IASTComment previous = leadingComments.get(0);
|
||||
for (int j = 1; j < leadingComments.size(); j++) {
|
||||
comment = leadingComments.get(j);
|
||||
if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1)
|
||||
return ASTNodes.skipToNextLineAfterNode(contents, previous);
|
||||
previous = comment;
|
||||
}
|
||||
if (getStartingLineNumber(node) > getEndingLineNumber(previous) + 1)
|
||||
return ASTNodes.skipToNextLineAfterNode(contents, previous);
|
||||
}
|
||||
node = inverseFreestandingMap.get(comment);
|
||||
if (node != null) {
|
||||
List<IASTComment> freestandingComments = commentMap.getFreestandingMap().get(node);
|
||||
IASTComment previous = freestandingComments.get(0);
|
||||
for (int j = 1; j < freestandingComments.size(); j++) {
|
||||
comment = freestandingComments.get(j);
|
||||
if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1)
|
||||
return ASTNodes.skipToNextLineAfterNode(contents, previous);
|
||||
previous = comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private Set<IBinding> removeBindingsDefinedInIncludedHeaders(IASTTranslationUnit ast,
|
||||
Set<IBinding> bindings, IIndexFileSet reachableHeaders) throws CoreException {
|
||||
List<InclusionRequest> requests = createInclusionRequests(ast, bindings, true, reachableHeaders);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012, 2013 Google, Inc and others.
|
||||
* Copyright (c) 2012, 2014 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
|
||||
|
@ -10,19 +10,45 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.ui.refactoring.includes;
|
||||
|
||||
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeEndOffset;
|
||||
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.content.IContentType;
|
||||
import org.eclipse.jface.text.IRegion;
|
||||
import org.eclipse.jface.text.Region;
|
||||
|
||||
import org.eclipse.cdt.core.CCorePlugin;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTComment;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorPragmaStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||
import org.eclipse.cdt.core.index.IIndexFile;
|
||||
import org.eclipse.cdt.core.index.IIndexFileLocation;
|
||||
import org.eclipse.cdt.core.index.IndexLocationFactory;
|
||||
import org.eclipse.cdt.core.parser.Keywords;
|
||||
import org.eclipse.cdt.core.parser.util.CharArrayIntMap;
|
||||
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
|
||||
|
||||
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.IncludeGuardDetection;
|
||||
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
|
||||
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
|
||||
import org.eclipse.cdt.internal.corext.codemanipulation.InclusionContext;
|
||||
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
|
||||
|
||||
public class IncludeUtil {
|
||||
/** Not instantiatable. All methods are static. */
|
||||
|
@ -72,7 +98,259 @@ public class IncludeUtil {
|
|||
}
|
||||
|
||||
public static boolean isContainedInRegion(IASTNode node, IRegion region) {
|
||||
return getNodeOffset(node) >= region.getOffset()
|
||||
&& getNodeEndOffset(node) <= region.getOffset() + region.getLength();
|
||||
return ASTNodes.offset(node) >= region.getOffset()
|
||||
&& ASTNodes.endOffset(node) <= region.getOffset() + region.getLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the region containing nothing but include statements located before the first
|
||||
* statement that may depend on includes.
|
||||
*
|
||||
* @param contents the contents of the translation unit
|
||||
* @param ast the AST
|
||||
* @param commentMap comments of the translation unit
|
||||
* @return the include region, possibly empty
|
||||
*/
|
||||
public static IRegion getSafeIncludeReplacementRegion(String contents, IASTTranslationUnit ast,
|
||||
NodeCommentMap commentMap) {
|
||||
int maxSafeOffset = ast.getFileLocation().getNodeLength();
|
||||
IASTDeclaration[] declarations = ast.getDeclarations(true);
|
||||
if (declarations.length != 0)
|
||||
maxSafeOffset = declarations[0].getFileLocation().getNodeOffset();
|
||||
|
||||
boolean topCommentSkipped = false;
|
||||
int includeOffset = -1;
|
||||
int includeEndOffset = -1;
|
||||
int includeGuardStatementsToSkip = getNumberOfIncludeGuardStatementsToSkip(contents, ast);
|
||||
int includeGuardEndOffset = -1;
|
||||
for (IASTPreprocessorStatement statement : ast.getAllPreprocessorStatements()) {
|
||||
if (statement.isPartOfTranslationUnitFile()) {
|
||||
IASTFileLocation fileLocation = statement.getFileLocation();
|
||||
int offset = fileLocation.getNodeOffset();
|
||||
if (offset >= maxSafeOffset)
|
||||
break;
|
||||
int endOffset = offset + fileLocation.getNodeLength();
|
||||
|
||||
if (includeGuardStatementsToSkip > 0) {
|
||||
--includeGuardStatementsToSkip;
|
||||
includeGuardEndOffset = endOffset;
|
||||
if (!commentMap.getLeadingCommentsForNode(statement).isEmpty()) {
|
||||
topCommentSkipped = true;
|
||||
}
|
||||
} else if (statement instanceof IASTPreprocessorIncludeStatement) {
|
||||
if (includeOffset < 0)
|
||||
includeOffset = offset;
|
||||
includeEndOffset = endOffset;
|
||||
includeGuardStatementsToSkip = 0; // Just in case
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (includeOffset < 0) {
|
||||
if (includeGuardEndOffset >= 0) {
|
||||
includeOffset = TextUtil.skipToNextLine(contents, includeGuardEndOffset);
|
||||
} else {
|
||||
includeOffset = 0;
|
||||
}
|
||||
if (!topCommentSkipped) {
|
||||
// Skip the first comment block near the top of the file.
|
||||
includeOffset = skipStandaloneCommentBlock(contents, includeOffset, maxSafeOffset,
|
||||
ast.getComments(), commentMap);
|
||||
}
|
||||
includeEndOffset = includeOffset;
|
||||
} else {
|
||||
includeEndOffset = TextUtil.skipToNextLine(contents, includeEndOffset);
|
||||
}
|
||||
return new Region(includeOffset, includeEndOffset - includeOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the include statements within the given region.
|
||||
*
|
||||
* @param existingIncludes the include statements to choose from
|
||||
* @param region the region to select includes within
|
||||
* @param inclusionContext the inclusion context
|
||||
* @return a list of {@link StyledInclude} objects representing the includes
|
||||
*/
|
||||
public static List<StyledInclude> getIncludesInRegion(IASTPreprocessorIncludeStatement[] existingIncludes,
|
||||
IRegion region, InclusionContext inclusionContext) {
|
||||
// Populate a list of existing includes in the include insertion region.
|
||||
List<StyledInclude> includes = new ArrayList<>();
|
||||
for (IASTPreprocessorIncludeStatement include : existingIncludes) {
|
||||
if (include.isPartOfTranslationUnitFile() && isContainedInRegion(include, region)) {
|
||||
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 ? inclusionContext.getIncludeStyle(header) : inclusionContext.getIncludeStyle(includeInfo);
|
||||
StyledInclude prototype = new StyledInclude(header, includeInfo, style, include);
|
||||
includes.add(prototype);
|
||||
}
|
||||
}
|
||||
return includes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the include guard in the file and, if found, returns its value and occurrences
|
||||
* in the file.
|
||||
*
|
||||
* @param contents the contents of the translation unit
|
||||
* @param ast the AST
|
||||
* @param includeGuardPositions the list of include guard occurrences that is populated by
|
||||
* the method
|
||||
* @return the include guard, or {@code null} if not found
|
||||
*/
|
||||
public static String findIncludeGuard(String contents, IASTTranslationUnit ast,
|
||||
List<IRegion> includeGuardPositions) {
|
||||
includeGuardPositions.clear();
|
||||
IASTPreprocessorStatement[] preprocessorStatements = ast.getAllPreprocessorStatements();
|
||||
int i = 0;
|
||||
while (true) {
|
||||
if (i >= preprocessorStatements.length)
|
||||
return null;
|
||||
if (preprocessorStatements[i].isPartOfTranslationUnitFile())
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
IASTPreprocessorStatement statement = preprocessorStatements[i];
|
||||
|
||||
int offset = 0;
|
||||
if (isPragmaOnce(statement)) {
|
||||
offset = ASTNodes.endOffset(statement);
|
||||
i++;
|
||||
}
|
||||
char[] guardChars = detectIncludeGuard(contents, offset);
|
||||
if (guardChars == null)
|
||||
return null;
|
||||
String guard = new String(guardChars);
|
||||
int count = 0;
|
||||
IASTPreprocessorStatement lastStatement = null;
|
||||
for (; i < preprocessorStatements.length; i++) {
|
||||
statement = preprocessorStatements[i];
|
||||
if (statement.isPartOfTranslationUnitFile()) {
|
||||
if (count < 2) {
|
||||
findGuardInRange(contents, guard, ASTNodes.offset(statement),
|
||||
ASTNodes.endOffset(statement), includeGuardPositions);
|
||||
count++;
|
||||
} else {
|
||||
lastStatement = statement;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastStatement != null) {
|
||||
findGuardInRange(contents, guard, ASTNodes.offset(lastStatement),
|
||||
contents.length(), includeGuardPositions);
|
||||
}
|
||||
return guard;
|
||||
}
|
||||
|
||||
private static int getNumberOfIncludeGuardStatementsToSkip(String contents, IASTTranslationUnit ast) {
|
||||
IASTPreprocessorStatement statement = findFirstPreprocessorStatement(ast);
|
||||
if (statement == null)
|
||||
return 0;
|
||||
|
||||
int num = 0;
|
||||
int offset = 0;
|
||||
if (isPragmaOnce(statement)) {
|
||||
num++;
|
||||
offset = ASTNodes.endOffset(statement);
|
||||
}
|
||||
char[] guard = detectIncludeGuard(contents, offset);
|
||||
if (guard != null) {
|
||||
num += 2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
private static char[] detectIncludeGuard(String contents, int offset) {
|
||||
char[] contentsChars = contents.toCharArray();
|
||||
if (offset != 0)
|
||||
contentsChars = Arrays.copyOfRange(contentsChars, offset, contentsChars.length);
|
||||
CharArrayIntMap ppKeywords= new CharArrayIntMap(40, -1);
|
||||
Keywords.addKeywordsPreprocessor(ppKeywords);
|
||||
char[] guardChars = IncludeGuardDetection.detectIncludeGuard(
|
||||
new CharArray(contentsChars), new LexerOptions(), ppKeywords);
|
||||
return guardChars;
|
||||
}
|
||||
|
||||
private static void findGuardInRange(String contents, String guard, int offset, int endOffset,
|
||||
List<IRegion> includeGuardPositions) {
|
||||
int pos = contents.indexOf(guard, offset);
|
||||
if (pos >= 0 && pos + guard.length() <= endOffset) {
|
||||
includeGuardPositions.add(new Region(pos, guard.length()));
|
||||
}
|
||||
}
|
||||
|
||||
private static int skipStandaloneCommentBlock(String contents, int offset, int endOffset,
|
||||
IASTComment[] comments, NodeCommentMap commentMap) {
|
||||
Map<IASTComment, IASTNode> inverseLeadingMap = new HashMap<>();
|
||||
for (Map.Entry<IASTNode, List<IASTComment>> entry : commentMap.getLeadingMap().entrySet()) {
|
||||
IASTNode node = entry.getKey();
|
||||
if (ASTNodes.offset(node) <= endOffset) {
|
||||
for (IASTComment comment : entry.getValue()) {
|
||||
inverseLeadingMap.put(comment, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
Map<IASTComment, IASTNode> inverseFreestandingMap = new HashMap<>();
|
||||
for (Map.Entry<IASTNode, List<IASTComment>> entry : commentMap.getFreestandingMap().entrySet()) {
|
||||
IASTNode node = entry.getKey();
|
||||
if (ASTNodes.endOffset(node) < endOffset) {
|
||||
for (IASTComment comment : entry.getValue()) {
|
||||
inverseFreestandingMap.put(comment, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < comments.length; i++) {
|
||||
IASTComment comment = comments[i];
|
||||
int commentOffset = ASTNodes.offset(comment);
|
||||
if (commentOffset >= offset) {
|
||||
if (commentOffset >= endOffset)
|
||||
break;
|
||||
IASTNode node = inverseLeadingMap.get(comment);
|
||||
if (node != null) {
|
||||
List<IASTComment> leadingComments = commentMap.getLeadingMap().get(node);
|
||||
IASTComment previous = leadingComments.get(0);
|
||||
for (int j = 1; j < leadingComments.size(); j++) {
|
||||
comment = leadingComments.get(j);
|
||||
if (ASTNodes.getStartingLineNumber(comment) > ASTNodes.getEndingLineNumber(previous) + 1)
|
||||
return ASTNodes.skipToNextLineAfterNode(contents, previous);
|
||||
previous = comment;
|
||||
}
|
||||
if (ASTNodes.getStartingLineNumber(node) > ASTNodes.getEndingLineNumber(previous) + 1)
|
||||
return ASTNodes.skipToNextLineAfterNode(contents, previous);
|
||||
}
|
||||
node = inverseFreestandingMap.get(comment);
|
||||
if (node != null) {
|
||||
List<IASTComment> freestandingComments = commentMap.getFreestandingMap().get(node);
|
||||
IASTComment previous = freestandingComments.get(0);
|
||||
for (int j = 1; j < freestandingComments.size(); j++) {
|
||||
comment = freestandingComments.get(j);
|
||||
if (ASTNodes.getStartingLineNumber(comment) > ASTNodes.getEndingLineNumber(previous) + 1)
|
||||
return ASTNodes.skipToNextLineAfterNode(contents, previous);
|
||||
previous = comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static IASTPreprocessorStatement findFirstPreprocessorStatement(IASTTranslationUnit ast) {
|
||||
for (IASTPreprocessorStatement statement : ast.getAllPreprocessorStatements()) {
|
||||
if (statement.isPartOfTranslationUnitFile())
|
||||
return statement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isPragmaOnce(IASTPreprocessorStatement statement) {
|
||||
if (!(statement instanceof IASTPreprocessorPragmaStatement))
|
||||
return false;
|
||||
return CharArrayUtils.equals(((IASTPreprocessorPragmaStatement) statement).getMessage(), "once"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 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.rename;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceProxy;
|
||||
import org.eclipse.core.resources.IResourceProxyVisitor;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.OperationCanceledException;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.ltk.core.refactoring.Change;
|
||||
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
|
||||
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
|
||||
import org.eclipse.ltk.core.refactoring.participants.MoveArguments;
|
||||
import org.eclipse.ltk.core.refactoring.participants.MoveParticipant;
|
||||
|
||||
/**
|
||||
* Updates include statements and include guards in response to a file or a folder move.
|
||||
*/
|
||||
public class HeaderFileMoveParticipant extends MoveParticipant {
|
||||
private IResource movedResource;
|
||||
private Change change;
|
||||
|
||||
public HeaderFileMoveParticipant() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean initialize(Object element) {
|
||||
if (element instanceof IResource) {
|
||||
this.movedResource = (IResource) element;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
|
||||
throws OperationCanceledException {
|
||||
MoveArguments args = getArguments();
|
||||
if (!args.getUpdateReferences())
|
||||
return null;
|
||||
if (movedResource.isLinked())
|
||||
return null;
|
||||
|
||||
Object destinationResource = args.getDestination();
|
||||
if (!(destinationResource instanceof IContainer))
|
||||
return null;
|
||||
final IContainer destination = (IContainer) destinationResource;
|
||||
final IPath destinationLocation = destination.getLocation();
|
||||
if (destinationLocation.equals(movedResource.getLocation().removeLastSegments(1)))
|
||||
return null;
|
||||
|
||||
try {
|
||||
// Maps the affected files to new, not yet existing, files.
|
||||
final Map<IFile, IFile> movedFiles = new HashMap<>();
|
||||
if (movedResource instanceof IContainer) {
|
||||
final int prefixLength = movedResource.getFullPath().segmentCount() - 1;
|
||||
((IContainer) movedResource).accept(new IResourceProxyVisitor() {
|
||||
@Override
|
||||
public boolean visit(IResourceProxy proxy) throws CoreException {
|
||||
if (proxy.isLinked())
|
||||
return false;
|
||||
if (proxy.getType() == IResource.FILE) {
|
||||
IFile file = (IFile) proxy.requestResource();
|
||||
movedFiles.put(file, destination.getFile(file.getFullPath().removeFirstSegments(prefixLength)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, IResource.NONE);
|
||||
} else if (movedResource instanceof IFile) {
|
||||
IFile file = (IFile) movedResource;
|
||||
movedFiles.put(file, destination.getFile(new Path(movedResource.getName())));
|
||||
}
|
||||
|
||||
HeaderFileReferenceAdjuster includeAdjuster = new HeaderFileReferenceAdjuster(movedFiles);
|
||||
change = includeAdjuster.createChange(context, pm);
|
||||
} catch (CoreException e) {
|
||||
return RefactoringStatus.create(e.getStatus());
|
||||
}
|
||||
return RefactoringStatus.create(Status.OK_STATUS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Change createPreChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
|
||||
pm.done();
|
||||
return change;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
|
||||
pm.done();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RenameMessages.HeaderFileMoveParticipant_name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,491 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 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.rename;
|
||||
|
||||
import static org.eclipse.cdt.internal.ui.editor.ASTProvider.WAIT_ACTIVE_ONLY;
|
||||
import static org.eclipse.cdt.internal.ui.editor.ASTProvider.getASTProvider;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IWorkspaceRoot;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.OperationCanceledException;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.core.runtime.SubMonitor;
|
||||
import org.eclipse.core.runtime.preferences.IPreferencesService;
|
||||
import org.eclipse.core.runtime.preferences.IScopeContext;
|
||||
import org.eclipse.jface.text.IRegion;
|
||||
import org.eclipse.ltk.core.refactoring.Change;
|
||||
import org.eclipse.ltk.core.refactoring.CompositeChange;
|
||||
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
|
||||
import org.eclipse.ltk.core.refactoring.participants.ValidateEditChecker;
|
||||
import org.eclipse.text.edits.DeleteEdit;
|
||||
import org.eclipse.text.edits.InsertEdit;
|
||||
import org.eclipse.text.edits.MultiTextEdit;
|
||||
import org.eclipse.text.edits.ReplaceEdit;
|
||||
import org.eclipse.text.edits.TextEdit;
|
||||
|
||||
import org.eclipse.cdt.core.CCorePlugin;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTComment;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||
import org.eclipse.cdt.core.index.IIndex;
|
||||
import org.eclipse.cdt.core.index.IIndexFile;
|
||||
import org.eclipse.cdt.core.index.IIndexFileLocation;
|
||||
import org.eclipse.cdt.core.index.IIndexInclude;
|
||||
import org.eclipse.cdt.core.index.IIndexManager;
|
||||
import org.eclipse.cdt.core.index.IndexLocationFactory;
|
||||
import org.eclipse.cdt.core.model.CoreModel;
|
||||
import org.eclipse.cdt.core.model.ICProject;
|
||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||
import org.eclipse.cdt.core.model.IWorkingCopy;
|
||||
import org.eclipse.cdt.ui.CUIPlugin;
|
||||
import org.eclipse.cdt.ui.IWorkingCopyManager;
|
||||
import org.eclipse.cdt.ui.PreferenceConstants;
|
||||
import org.eclipse.cdt.ui.refactoring.CTextFileChange;
|
||||
|
||||
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.corext.codemanipulation.IncludeInfo;
|
||||
import org.eclipse.cdt.internal.corext.codemanipulation.StubUtility;
|
||||
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
|
||||
|
||||
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeCreationContext;
|
||||
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle;
|
||||
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences;
|
||||
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeUtil;
|
||||
|
||||
/**
|
||||
* Updates include statements and include guards in response to file or folder move or rename.
|
||||
*/
|
||||
public class HeaderFileReferenceAdjuster {
|
||||
private static final int PARSE_MODE = ITranslationUnit.AST_SKIP_ALL_HEADERS
|
||||
| ITranslationUnit.AST_CONFIGURE_USING_SOURCE_CONTEXT
|
||||
| ITranslationUnit.AST_SKIP_FUNCTION_BODIES
|
||||
| ITranslationUnit.AST_PARSE_INACTIVE_CODE;
|
||||
|
||||
private final Map<IFile, IFile> movedFiles;
|
||||
private final Map<String, IPath> movedFilesByLocation;
|
||||
private IIndex index;
|
||||
private int indexLockCount;
|
||||
|
||||
/**
|
||||
* @param movedFiles keys are moved files, values are new, not yet existing, files
|
||||
*/
|
||||
public HeaderFileReferenceAdjuster(Map<IFile, IFile> movedFiles) {
|
||||
this.movedFiles = movedFiles;
|
||||
this.movedFilesByLocation = new HashMap<>();
|
||||
for (Entry<IFile, IFile> entry : movedFiles.entrySet()) {
|
||||
this.movedFilesByLocation.put(entry.getKey().getLocation().toOSString(), entry.getValue().getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
public Change createChange(CheckConditionsContext context, IProgressMonitor pm)
|
||||
throws CoreException, OperationCanceledException {
|
||||
SubMonitor progress = SubMonitor.convert(pm, 10);
|
||||
CompositeChange change = null;
|
||||
Set<IFile> affectedFiles = new HashSet<>();
|
||||
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
|
||||
|
||||
lockIndex();
|
||||
try {
|
||||
for (Entry<IFile, IFile> entry : movedFiles.entrySet()) {
|
||||
IFile oldFile = entry.getKey();
|
||||
IFile newFile = entry.getValue();
|
||||
if (areIncludeGuardsAffected(oldFile, newFile))
|
||||
affectedFiles.add(oldFile);
|
||||
|
||||
IIndexFileLocation indexFileLocation = IndexLocationFactory.getWorkspaceIFL(oldFile);
|
||||
IIndexFile[] indexFiles = index.getFiles(indexFileLocation);
|
||||
for (IIndexFile indexFile : indexFiles) {
|
||||
IIndexInclude[] includes = index.findIncludedBy(indexFile);
|
||||
for (IIndexInclude include : includes) {
|
||||
IIndexFileLocation includeLocation = include.getIncludedByLocation();
|
||||
String path = includeLocation.getFullPath();
|
||||
if (path != null) {
|
||||
IResource resource = workspaceRoot.findMember(path);
|
||||
if (resource.getType() == IResource.FILE) {
|
||||
IFile includer = (IFile) resource;
|
||||
affectedFiles.add(includer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IWorkingCopyManager workingCopyManager = CUIPlugin.getDefault().getWorkingCopyManager();
|
||||
IWorkingCopy[] workingCopies = workingCopyManager.getSharedWorkingCopies();
|
||||
progress.worked(1);
|
||||
progress = SubMonitor.convert(progress.newChild(9), workingCopies.length + affectedFiles.size());
|
||||
|
||||
List<Change> changes = new ArrayList<>();
|
||||
ValidateEditChecker checker= (ValidateEditChecker) context.getChecker(ValidateEditChecker.class);
|
||||
for (ITranslationUnit tu : workingCopies) {
|
||||
addFileChange(tu, changes, checker, progress.newChild(1));
|
||||
}
|
||||
|
||||
CoreModel coreModel = CoreModel.getDefault();
|
||||
for (IFile file : affectedFiles) {
|
||||
ITranslationUnit tu = (ITranslationUnit) coreModel.create(file);
|
||||
if (workingCopyManager.findSharedWorkingCopy(tu) != null)
|
||||
continue;
|
||||
addFileChange(tu, changes, checker, progress.newChild(1));
|
||||
}
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
change = new CompositeChange("", changes.toArray(new Change[changes.size()])); //$NON-NLS-1$
|
||||
change.markAsSynthetic();
|
||||
}
|
||||
} finally {
|
||||
unlockIndex();
|
||||
pm.done();
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
private void addFileChange(ITranslationUnit tu, List<Change> changes, ValidateEditChecker checker,
|
||||
IProgressMonitor pm) throws CoreException {
|
||||
TextEdit edit = createEdit(tu, pm);
|
||||
if (edit != null) {
|
||||
CTextFileChange fileChange = new CTextFileChange(tu.getElementName(), tu);
|
||||
fileChange.setEdit(edit);
|
||||
changes.add(fileChange);
|
||||
checker.addFile(fileChange.getFile());
|
||||
}
|
||||
}
|
||||
|
||||
private TextEdit createEdit(ITranslationUnit tu, IProgressMonitor pm)
|
||||
throws CoreException, OperationCanceledException {
|
||||
checkCanceled(pm);
|
||||
|
||||
IASTTranslationUnit sharedAst = null;
|
||||
|
||||
SubMonitor progress = SubMonitor.convert(pm, 2);
|
||||
try {
|
||||
IASTTranslationUnit ast =
|
||||
getASTProvider().acquireSharedAST(tu, index, WAIT_ACTIVE_ONLY, progress.newChild(1));
|
||||
if (ast == null) {
|
||||
checkCanceled(pm);
|
||||
ast= tu.getAST(index, PARSE_MODE);
|
||||
if (ast == null)
|
||||
return null;
|
||||
} else {
|
||||
sharedAst = ast;
|
||||
}
|
||||
return createEdit(ast, tu, progress.newChild(1));
|
||||
} finally {
|
||||
if (sharedAst != null) {
|
||||
getASTProvider().releaseSharedAST(sharedAst);
|
||||
}
|
||||
pm.done();
|
||||
}
|
||||
}
|
||||
|
||||
private TextEdit createEdit(IASTTranslationUnit ast, ITranslationUnit tu, IProgressMonitor pm)
|
||||
throws CoreException, OperationCanceledException {
|
||||
IncludeCreationContext context = new IncludeCreationContext(tu, index);
|
||||
String contents = context.getSourceContents();
|
||||
|
||||
MultiTextEdit rootEdit = createIncludeGuardEdit(ast, tu, contents);
|
||||
|
||||
Map<IASTPreprocessorIncludeStatement, IPath> affectedIncludes = new IdentityHashMap<>();
|
||||
IASTPreprocessorIncludeStatement[] existingIncludes = ast.getIncludeDirectives();
|
||||
for (IASTPreprocessorIncludeStatement include : existingIncludes) {
|
||||
if (include.isPartOfTranslationUnitFile()) {
|
||||
String location;
|
||||
if (include.isActive()) {
|
||||
location = include.getPath();
|
||||
if (location.isEmpty())
|
||||
continue; // Unresolved include.
|
||||
} else {
|
||||
String name = new String(include.getName().getSimpleID());
|
||||
IncludeInfo includeInfo = new IncludeInfo(name, include.isSystemInclude());
|
||||
IPath path = context.resolveInclude(includeInfo);
|
||||
if (path == null)
|
||||
continue;
|
||||
location = path.toString();
|
||||
}
|
||||
IPath newLocation = movedFilesByLocation.get(location);
|
||||
if (newLocation != null) {
|
||||
affectedIncludes.put(include, newLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (affectedIncludes.isEmpty())
|
||||
return rootEdit;
|
||||
|
||||
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
|
||||
IRegion includeRegion =
|
||||
IncludeUtil.getSafeIncludeReplacementRegion(contents, ast, commentedNodeMap);
|
||||
|
||||
IncludePreferences preferences = context.getPreferences();
|
||||
|
||||
if (rootEdit == null)
|
||||
rootEdit = new MultiTextEdit();
|
||||
|
||||
context.addHeadersIncludedPreviously(existingIncludes);
|
||||
|
||||
if (preferences.allowReordering) {
|
||||
List<StyledInclude> modifiedIncludes = new ArrayList<>();
|
||||
// Put the changed includes into modifiedIncludes.
|
||||
for (Entry<IASTPreprocessorIncludeStatement, IPath> entry : affectedIncludes.entrySet()) {
|
||||
IASTPreprocessorIncludeStatement existingInclude = entry.getKey();
|
||||
if (IncludeUtil.isContainedInRegion(existingInclude, includeRegion)) {
|
||||
IPath header = entry.getValue();
|
||||
IncludeGroupStyle style = context.getIncludeStyle(header);
|
||||
IncludeInfo includeInfo = context.createIncludeInfo(header, style);
|
||||
StyledInclude include = new StyledInclude(header, includeInfo, style, existingInclude);
|
||||
modifiedIncludes.add(include);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(modifiedIncludes, preferences);
|
||||
|
||||
// Populate a list of the existing unchanged includes in the include insertion region.
|
||||
List<StyledInclude> mergedIncludes =
|
||||
IncludeUtil.getIncludesInRegion(existingIncludes, includeRegion, context);
|
||||
Deque<DeleteEdit> deletes = new ArrayDeque<>();
|
||||
// Create text deletes for old locations of the includes that will be changed.
|
||||
int deleteOffset = -1;
|
||||
boolean emptyLineEncountered = false;
|
||||
int j = 0;
|
||||
for (int i = 0; i < mergedIncludes.size(); i++) {
|
||||
StyledInclude include = mergedIncludes.get(i);
|
||||
IASTPreprocessorIncludeStatement existingInclude = include.getExistingInclude();
|
||||
int offset = ASTNodes.offset(existingInclude);
|
||||
boolean previousLineBlank = TextUtil.isPreviousLineBlank(contents, offset);
|
||||
if (affectedIncludes.containsKey(existingInclude)) {
|
||||
if (deleteOffset < 0) {
|
||||
deleteOffset = offset;
|
||||
} else if (!emptyLineEncountered && previousLineBlank) {
|
||||
// Preserve the first encountered blank line.
|
||||
deletes.add(new DeleteEdit(deleteOffset, offset - deleteOffset));
|
||||
deleteOffset = -1;
|
||||
}
|
||||
emptyLineEncountered |= previousLineBlank;
|
||||
} else {
|
||||
if (deleteOffset >= 0) {
|
||||
if (!emptyLineEncountered && previousLineBlank) {
|
||||
offset = TextUtil.getPreviousLineStart(contents, offset);
|
||||
}
|
||||
deletes.add(new DeleteEdit(deleteOffset, offset - deleteOffset));
|
||||
deleteOffset = -1;
|
||||
}
|
||||
emptyLineEncountered = false;
|
||||
if (j < i)
|
||||
mergedIncludes.set(j, include);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
while (j < mergedIncludes.size()) {
|
||||
mergedIncludes.remove(mergedIncludes.size() - 1);
|
||||
}
|
||||
if (deleteOffset >= 0)
|
||||
deletes.add(new DeleteEdit(deleteOffset, includeRegion.getOffset() + includeRegion.getLength() - deleteOffset));
|
||||
|
||||
// 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 up
|
||||
// from the bottom of the include insertion region.
|
||||
for (StyledInclude include : modifiedIncludes) {
|
||||
if (IncludeUtil.isContainedInRegion(include.getExistingInclude(), includeRegion)) {
|
||||
int i = mergedIncludes.size();
|
||||
while (--i >= 0 && preferences.compare(include, mergedIncludes.get(i)) < 0) {}
|
||||
mergedIncludes.add(i + 1, include);
|
||||
}
|
||||
}
|
||||
|
||||
int offset = includeRegion.getOffset();
|
||||
StringBuilder text = new StringBuilder();
|
||||
StyledInclude previousInclude = null;
|
||||
for (StyledInclude include : mergedIncludes) {
|
||||
IASTPreprocessorIncludeStatement existingInclude = include.getExistingInclude();
|
||||
if (affectedIncludes.containsKey(existingInclude)) {
|
||||
if (previousInclude != null) {
|
||||
IASTNode previousNode = previousInclude.getExistingInclude();
|
||||
if (!affectedIncludes.containsKey(previousNode)) {
|
||||
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
|
||||
flushEditBuffer(offset, text, deletes, rootEdit);
|
||||
if (contents.charAt(offset - 1) != '\n')
|
||||
text.append(context.getLineDelimiter());
|
||||
}
|
||||
if (isBlankLineNeededBetween(previousInclude, include, preferences)) {
|
||||
if (TextUtil.isLineBlank(contents, offset)) {
|
||||
int oldOffset = offset;
|
||||
offset = TextUtil.skipToNextLine(contents, offset);
|
||||
if (offset == oldOffset || contents.charAt(offset - 1) != '\n')
|
||||
text.append(context.getLineDelimiter());
|
||||
} else {
|
||||
text.append(context.getLineDelimiter());
|
||||
}
|
||||
}
|
||||
}
|
||||
text.append(include.getIncludeInfo().composeIncludeStatement());
|
||||
List<IASTComment> comments = commentedNodeMap.getTrailingCommentsForNode(existingInclude);
|
||||
for (IASTComment comment : comments) {
|
||||
text.append(ASTNodes.getPrecedingWhitespaceInLine(contents, comment));
|
||||
text.append(comment.getRawSignature());
|
||||
}
|
||||
text.append(context.getLineDelimiter());
|
||||
} else {
|
||||
if (previousInclude != null && affectedIncludes.containsKey(previousInclude.getExistingInclude()) &&
|
||||
isBlankLineNeededBetween(previousInclude, include, preferences) &&
|
||||
TextUtil.findBlankLine(contents, offset, ASTNodes.offset(existingInclude)) < 0) {
|
||||
text.append(context.getLineDelimiter());
|
||||
}
|
||||
flushEditBuffer(offset, text, deletes, rootEdit);
|
||||
}
|
||||
previousInclude = include;
|
||||
}
|
||||
if (includeRegion.getLength() == 0 && !TextUtil.isLineBlank(contents, includeRegion.getOffset())) {
|
||||
text.append(context.getLineDelimiter());
|
||||
}
|
||||
offset = includeRegion.getOffset() + includeRegion.getLength();
|
||||
flushEditBuffer(offset, text, deletes, rootEdit);
|
||||
}
|
||||
|
||||
for (IASTPreprocessorIncludeStatement existingInclude : existingIncludes) {
|
||||
IPath header = affectedIncludes.get(existingInclude);
|
||||
if (header != null &&
|
||||
(!preferences.allowReordering || !IncludeUtil.isContainedInRegion(existingInclude, includeRegion))) {
|
||||
IncludeGroupStyle style = context.getIncludeStyle(header);
|
||||
IncludeInfo includeInfo = context.createIncludeInfo(header, style);
|
||||
IASTName name = existingInclude.getName();
|
||||
int offset = ASTNodes.offset(name) - 1;
|
||||
int length = ASTNodes.endOffset(name) + 1 - offset;
|
||||
rootEdit.addChild(new ReplaceEdit(offset, length, includeInfo.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
return rootEdit;
|
||||
}
|
||||
|
||||
private static boolean isBlankLineNeededBetween(StyledInclude include1, StyledInclude include2,
|
||||
IncludePreferences preferences) {
|
||||
return include2.getStyle().isBlankLineNeededAfter(include1.getStyle(), preferences.includeStyles);
|
||||
}
|
||||
|
||||
private MultiTextEdit createIncludeGuardEdit(IASTTranslationUnit ast, ITranslationUnit tu, String contents) {
|
||||
IResource resource = tu.getResource();
|
||||
IFile newFile = movedFiles.get(resource);
|
||||
if (newFile == null)
|
||||
return null;
|
||||
boolean guardsAffected = areIncludeGuardsAffected((IFile) resource, newFile);
|
||||
if (!guardsAffected)
|
||||
return null;
|
||||
List<IRegion> includeGuardPositions = new ArrayList<>();
|
||||
String oldGuard = IncludeUtil.findIncludeGuard(contents, ast, includeGuardPositions);
|
||||
if (oldGuard == null)
|
||||
return null;
|
||||
if (!oldGuard.equals(StubUtility.generateIncludeGuardSymbol(resource, tu.getCProject())))
|
||||
return null;
|
||||
IProject newProject = newFile.getProject();
|
||||
ICProject newCProject = CoreModel.getDefault().create(newProject);
|
||||
if (newCProject == null)
|
||||
return null;
|
||||
String guard = StubUtility.generateIncludeGuardSymbol(newFile, newCProject);
|
||||
if (guard.equals(oldGuard))
|
||||
return null;
|
||||
MultiTextEdit rootEdit = new MultiTextEdit();
|
||||
for (IRegion region : includeGuardPositions) {
|
||||
rootEdit.addChild(new ReplaceEdit(region.getOffset(), region.getLength(), guard));
|
||||
}
|
||||
return rootEdit;
|
||||
}
|
||||
|
||||
private void flushEditBuffer(int offset, StringBuilder text, Deque<DeleteEdit> deletes, MultiTextEdit edit) {
|
||||
consumeDeletesUpTo(offset, deletes, edit);
|
||||
if (text.length() != 0) {
|
||||
edit.addChild(new InsertEdit(offset, text.toString()));
|
||||
text.delete(0, text.length());
|
||||
}
|
||||
}
|
||||
|
||||
private void consumeDeletesUpTo(int offset, Deque<DeleteEdit> deletes, MultiTextEdit rootEdit) {
|
||||
while (!deletes.isEmpty()) {
|
||||
DeleteEdit edit = deletes.peek();
|
||||
if (edit.getOffset() > offset)
|
||||
break;
|
||||
deletes.remove();
|
||||
rootEdit.addChild(edit);
|
||||
}
|
||||
}
|
||||
|
||||
private void lockIndex() throws CoreException, OperationCanceledException {
|
||||
if (indexLockCount == 0) {
|
||||
if (index == null) {
|
||||
ICProject[] projects= CoreModel.getDefault().getCModel().getCProjects();
|
||||
index = CCorePlugin.getIndexManager().getIndex(projects,
|
||||
IIndexManager.ADD_EXTENSION_FRAGMENTS_EDITOR);
|
||||
}
|
||||
try {
|
||||
index.acquireReadLock();
|
||||
} catch (InterruptedException e) {
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
}
|
||||
indexLockCount++;
|
||||
}
|
||||
|
||||
private void unlockIndex() {
|
||||
if (--indexLockCount <= 0) {
|
||||
if (index != null) {
|
||||
index.releaseReadLock();
|
||||
}
|
||||
index = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkCanceled(IProgressMonitor pm) throws OperationCanceledException {
|
||||
if (pm != null && pm.isCanceled())
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
private static boolean areIncludeGuardsAffected(IFile oldfile, IFile newFile) {
|
||||
String filename = oldfile.getLocation().lastSegment();
|
||||
if (!CoreModel.isValidHeaderUnitName(null, filename))
|
||||
return false;
|
||||
IPreferencesService preferences = Platform.getPreferencesService();
|
||||
IScopeContext[] scopes = PreferenceConstants.getPreferenceScopes(oldfile.getProject());
|
||||
int schema = preferences.getInt(CUIPlugin.PLUGIN_ID,
|
||||
PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME,
|
||||
PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_NAME, scopes);
|
||||
switch (schema) {
|
||||
case PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_PATH:
|
||||
return true;
|
||||
|
||||
case PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_NAME:
|
||||
return !filename.equals(newFile.getName());
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 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.rename;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceProxy;
|
||||
import org.eclipse.core.resources.IResourceProxyVisitor;
|
||||
import org.eclipse.core.resources.IWorkspaceRoot;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.OperationCanceledException;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.ltk.core.refactoring.Change;
|
||||
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
|
||||
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
|
||||
import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
|
||||
import org.eclipse.ltk.core.refactoring.participants.RenameParticipant;
|
||||
|
||||
/**
|
||||
* Updates include statements and include guards in response to a file or a folder rename.
|
||||
*/
|
||||
public class HeaderFileRenameParticipant extends RenameParticipant {
|
||||
private IResource renamedResource;
|
||||
private Change change;
|
||||
|
||||
public HeaderFileRenameParticipant() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean initialize(Object element) {
|
||||
if (element instanceof IResource) {
|
||||
this.renamedResource = (IResource) element;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
|
||||
throws OperationCanceledException {
|
||||
RenameArguments args = getArguments();
|
||||
if (!args.getUpdateReferences())
|
||||
return null;
|
||||
if (renamedResource.isLinked())
|
||||
return null;
|
||||
|
||||
String newName = args.getNewName();
|
||||
|
||||
try {
|
||||
// Maps the affected files to new, not yet existing, files.
|
||||
final Map<IFile, IFile> movedFiles = new HashMap<>();
|
||||
if (renamedResource instanceof IContainer) {
|
||||
final IPath oldPath = renamedResource.getFullPath();
|
||||
final IPath newPath = oldPath.removeLastSegments(1).append(newName);
|
||||
final IWorkspaceRoot workspaceRoot = renamedResource.getWorkspace().getRoot();
|
||||
((IContainer) renamedResource).accept(new IResourceProxyVisitor() {
|
||||
@Override
|
||||
public boolean visit(IResourceProxy proxy) throws CoreException {
|
||||
if (proxy.isLinked())
|
||||
return false;
|
||||
if (proxy.getType() == IResource.FILE) {
|
||||
IFile file = (IFile) proxy.requestResource();
|
||||
IPath path = replacePrefix(file.getFullPath(), oldPath.segmentCount(), newPath);
|
||||
movedFiles.put(file, workspaceRoot.getFile(path));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, IResource.NONE);
|
||||
} else if (renamedResource instanceof IFile) {
|
||||
IFile file = (IFile) renamedResource;
|
||||
movedFiles.put(file, file.getParent().getFile(new Path(newName)));
|
||||
}
|
||||
|
||||
HeaderFileReferenceAdjuster includeAdjuster = new HeaderFileReferenceAdjuster(movedFiles);
|
||||
change = includeAdjuster.createChange(context, pm);
|
||||
} catch (CoreException e) {
|
||||
return RefactoringStatus.create(e.getStatus());
|
||||
}
|
||||
return RefactoringStatus.create(Status.OK_STATUS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Change createPreChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
|
||||
pm.done();
|
||||
return change;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
|
||||
pm.done();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RenameMessages.HeaderFileRenameParticipant_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces first few segments of the given path by the contents of another path.
|
||||
*
|
||||
* @param path the original path
|
||||
* @param prefixLength the number of segments of {@code path} to replace
|
||||
* @param newPrefix the replacement path
|
||||
* @return the modified path
|
||||
* @since 5.8
|
||||
*/
|
||||
private static IPath replacePrefix(IPath path, int prefixLength, IPath newPrefix) {
|
||||
return newPrefix.append(path.removeFirstSegments(prefixLength));
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2004, 2010 Wind River Systems, Inc.
|
||||
* Copyright (c) 2004, 2014 Wind River Systems, Inc.
|
||||
* 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
|
||||
|
@ -97,9 +97,11 @@ class RenameMessages extends NLS {
|
|||
public static String CRenameTopProcessor_virtualMethod;
|
||||
public static String CRenameTopProcessor_wizard_backup_title;
|
||||
public static String CRenameTopProcessor_wizard_title;
|
||||
public static String HeaderFileMoveParticipant_name;
|
||||
public static String HeaderFileRenameParticipant_name;
|
||||
public static String RenameCSourceFolderChange_ErrorMsg;
|
||||
public static String RenameCSourceFolderChange_Name0;
|
||||
public static String RenameSourceFolder_0;
|
||||
public static String SourceFolderRenameParticipant_name;
|
||||
public static String RenameInformationPopup_delayJobName;
|
||||
public static String RenameInformationPopup_EnterNewName;
|
||||
public static String RenameInformationPopup_menu;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
###############################################################################
|
||||
# Copyright (c) 2005, 2010 IBM Corporation and others.
|
||||
# Copyright (c) 2005, 2014 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
|
||||
|
@ -10,7 +10,7 @@
|
|||
# Markus Schorn, Wind River Systems Inc.
|
||||
# Sergey Prigogin (Google)
|
||||
###############################################################################
|
||||
ASTManager_error_macro_name_conflict=''{0}'' conflicts with the name of an existing macro!
|
||||
ASTManager_error_macro_name_conflict=''{0}'' conflicts with the name of an existing macro.
|
||||
ASTManager_subtask_analyzing=Analyzing {0} files
|
||||
ASTManager_task_analyze=Analyzing source code
|
||||
ASTManager_task_generateAst=Generating AST
|
||||
|
@ -93,9 +93,11 @@ CRenameTopProcessor_type=type
|
|||
CRenameTopProcessor_virtualMethod=virtual method
|
||||
CRenameTopProcessor_wizard_backup_title=Rename
|
||||
CRenameTopProcessor_wizard_title=Rename ''{0}''
|
||||
HeaderFileMoveParticipant_name=Header File Move
|
||||
HeaderFileRenameParticipant_name=Header File Rename
|
||||
RenameCSourceFolderChange_ErrorMsg=Folder {0} does not exist
|
||||
RenameCSourceFolderChange_Name0=Rename source folder {0} to {1}
|
||||
RenameSourceFolder_0=Rename C/C++ Source Folder
|
||||
SourceFolderRenameParticipant_name=Rename C/C++ Source Folder
|
||||
RenameInformationPopup_SnapTo=&Snap To
|
||||
RenameInformationPopup_snap_under_left=&Under Left
|
||||
RenameInformationPopup_snap_under_right=U&nder Right
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009 Institute for Software, HSR Hochschule fuer Technik
|
||||
* Copyright (c) 2009, 2014 Institute for Software, HSR Hochschule fuer Technik
|
||||
* Rapperswil, University of applied sciences and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
@ -20,37 +20,36 @@ import org.eclipse.core.runtime.Status;
|
|||
import org.eclipse.ltk.core.refactoring.Change;
|
||||
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
|
||||
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
|
||||
import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
|
||||
import org.eclipse.ltk.core.refactoring.participants.RenameParticipant;
|
||||
|
||||
/**
|
||||
* Updates source folders and associated filters of a C/C++ project in response to a folder rename.
|
||||
*
|
||||
* @author Emanuel Graf IFS
|
||||
*/
|
||||
public class RenameSourceFolder extends RenameParticipant {
|
||||
public class SourceFolderRenameParticipant extends RenameParticipant {
|
||||
private IFolder oldFolder;
|
||||
private String newName;
|
||||
|
||||
public RenameSourceFolder() {
|
||||
public SourceFolderRenameParticipant() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
|
||||
throws OperationCanceledException {
|
||||
RenameArguments arg = getArguments();
|
||||
newName = arg.getNewName();
|
||||
return RefactoringStatus.create(Status.OK_STATUS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
|
||||
IPath oldFolderPath = oldFolder.getFullPath();
|
||||
IPath newFolderPath = oldFolder.getFullPath().uptoSegment(oldFolder.getFullPath().segmentCount() - 1).append(newName);
|
||||
String newName = getArguments().getNewName();
|
||||
IPath newFolderPath = oldFolderPath.removeLastSegments(1).append(newName);
|
||||
return new RenameCSourceFolderChange(oldFolderPath, newFolderPath, oldFolder.getProject(), oldFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RenameMessages.RenameSourceFolder_0;
|
||||
return RenameMessages.SourceFolderRenameParticipant_name;
|
||||
}
|
||||
|
||||
@Override
|
|
@ -2347,7 +2347,7 @@ public class PreferenceConstants {
|
|||
|
||||
// Code Templates
|
||||
store.setDefault(PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME,
|
||||
CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_NAME);
|
||||
CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_PATH);
|
||||
|
||||
// Name Style
|
||||
store.setDefault(NAME_STYLE_CONSTANT_CAPITALIZATION, NAME_STYLE_CAPITALIZATION_UPPER_CASE);
|
||||
|
|
|
@ -59,6 +59,8 @@ public class CTextFileChange extends TextFileChange {
|
|||
public CTextFileChange(String name, ITranslationUnit tu) {
|
||||
super(name, getFile(tu));
|
||||
fTranslationUnit = tu;
|
||||
if (tu instanceof IWorkingCopy)
|
||||
fWorkingCopy = (IWorkingCopy) tu;
|
||||
setTextType(TEXT_TYPE);
|
||||
}
|
||||
|
||||
|
@ -66,7 +68,7 @@ public class CTextFileChange extends TextFileChange {
|
|||
protected IDocument acquireDocument(IProgressMonitor pm) throws CoreException {
|
||||
IDocument doc= super.acquireDocument(pm);
|
||||
if (++fAcquireCount == 1) {
|
||||
if (fTranslationUnit instanceof TranslationUnit && fWorkingCopy == null) {
|
||||
if (fWorkingCopy == null && fTranslationUnit instanceof TranslationUnit) {
|
||||
fWorkingCopy= ((TranslationUnit) fTranslationUnit).getWorkingCopy(null, DocumentAdapter.FACTORY);
|
||||
if (!fTranslationUnit.isOpen()) {
|
||||
fTranslationUnit.open(null);
|
||||
|
@ -89,7 +91,7 @@ public class CTextFileChange extends TextFileChange {
|
|||
protected void releaseDocument(IDocument document, IProgressMonitor pm) throws CoreException {
|
||||
super.releaseDocument(document, pm);
|
||||
if (--fAcquireCount == 0) {
|
||||
if (fWorkingCopy != null) {
|
||||
if (fWorkingCopy != null && fWorkingCopy != fTranslationUnit) {
|
||||
fWorkingCopy.destroy();
|
||||
fWorkingCopy= null;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue