1
0
Fork 0
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:
Sergey Prigogin 2014-06-16 16:27:00 -07:00
parent cf4556f05f
commit d962e2eee7
30 changed files with 1580 additions and 379 deletions

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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$
}
}

View file

@ -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;
}
}

View file

@ -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>

View file

@ -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");

View file

@ -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);
}
}

View file

@ -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();
}
}
}

View file

@ -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;

View file

@ -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();
}
}

View file

@ -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

View file

@ -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>

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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();

View file

@ -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();

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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$
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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));
}
}

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;
}