Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergey Prigogin2012-04-04 20:20:42 -0400
committerSergey Prigogin2012-04-04 20:27:13 -0400
commit1d231890fbf07054888ad6903005439501c427b0 (patch)
treeb27891a8b99943a069a343468185215d7b3d968c /core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite
parent3e38bde42ce3d1b8c019fff9f8d25b69f8730b0a (diff)
downloadorg.eclipse.cdt-1d231890fbf07054888ad6903005439501c427b0.tar.gz
org.eclipse.cdt-1d231890fbf07054888ad6903005439501c427b0.tar.xz
org.eclipse.cdt-1d231890fbf07054888ad6903005439501c427b0.zip
Bug 255093 - In C++ refactoring, selection should be updated like Java
refactorings do
Diffstat (limited to 'core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite')
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java222
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/TextEditUtil.java219
2 files changed, 385 insertions, 56 deletions
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java
index 8bf7c9c7b2..63621e08d9 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java
@@ -72,6 +72,8 @@ import org.eclipse.jface.text.TextUtilities;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.text.edits.DeleteEdit;
+import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
@@ -312,6 +314,37 @@ public class ChangeGenerator extends ASTVisitor {
return super.leave(statement);
}
+ private void addChildEdit(TextEdit edit) {
+ rootEdit.addChild(edit);
+ processedOffset = edit.getExclusiveEnd();
+ }
+
+ private TextEdit clippedEdit(TextEdit edit, IRegion region) {
+ if ((edit.getOffset() < region.getOffset() && edit.getExclusiveEnd() <= region.getOffset()) ||
+ edit.getOffset() >= endOffset(region)) {
+ return null;
+ }
+ int offset = Math.max(edit.getOffset(), region.getOffset());
+ int length = Math.min(endOffset(edit), endOffset(region)) - offset;
+ if (offset == edit.getOffset() && length == edit.getLength()) {
+ // InsertEdit always satisfies the above condition.
+ return edit;
+ }
+ if (edit instanceof DeleteEdit) {
+ return new DeleteEdit(offset, length);
+ } if (edit instanceof ReplaceEdit) {
+ String replacement = ((ReplaceEdit) edit).getText();
+ int start = Math.max(offset - edit.getOffset(), 0);
+ int end = Math.min(endOffset(region) - offset, replacement.length());
+ if (end <= start) {
+ return new DeleteEdit(offset, length);
+ }
+ return new ReplaceEdit(offset, length, replacement.substring(start, end));
+ } else {
+ throw new IllegalArgumentException("Unexpected edit type: " + edit.getClass().getSimpleName()); //$NON-NLS-1$
+ }
+ }
+
/**
* Applies the C++ code formatter to the code affected by refactoring.
*
@@ -321,47 +354,40 @@ public class ChangeGenerator extends ASTVisitor {
private void formatChangedCode(String code, ITranslationUnit tu) {
IDocument document = new Document(code);
try {
- // Apply refactoring changes to a temporary document.
TextEdit edit = rootEdit.copy();
+ // Apply refactoring changes to a temporary document.
edit.apply(document, TextEdit.UPDATE_REGIONS);
// Expand regions affected by the changes to cover complete lines. We calculate two
// sets of regions, reflecting the state of the document before and after
// the refactoring changes.
- TextEdit[] edits = edit.getChildren();
- TextEdit[] originalEdits = rootEdit.getChildren();
- IRegion[] regionsAfter = new IRegion[edits.length];
- IRegion[] regionsBefore = new IRegion[edits.length];
+ TextEdit[] appliedEdits = edit.getChildren();
+ TextEdit[] edits = rootEdit.removeChildren();
+ IRegion[] regions = new IRegion[appliedEdits.length];
int numRegions = 0;
int prevEnd = -1;
- for (int i = 0; i < edits.length; i++) {
- edit = edits[i];
+ for (int i = 0; i < appliedEdits.length; i++) {
+ edit = appliedEdits[i];
int offset = edit.getOffset();
int end = offset + edit.getLength();
int newOffset = document.getLineInformationOfOffset(offset).getOffset();
- edit = originalEdits[i];
+ edit = edits[i];
int originalEnd = edit.getExclusiveEnd();
// Expand to the end of the line unless the end of the edit region is at
// the beginning of line both, before and after the change.
int newEnd = (originalEnd == 0 || code.charAt(originalEnd - 1) == '\n') && end == newOffset ?
end : endOffset(document.getLineInformationOfOffset(end));
- int offsetBefore = edit.getOffset();
- int newOffsetBefore = newOffset + offsetBefore - offset;
- int newEndBefore = newEnd + offsetBefore + edit.getLength() - end;
if (newOffset <= prevEnd) {
numRegions--;
- newOffset = regionsAfter[numRegions].getOffset();
- newOffsetBefore = regionsBefore[numRegions].getOffset();
+ newOffset = regions[numRegions].getOffset();
}
prevEnd = newEnd;
- regionsAfter[numRegions] = new Region(newOffset, newEnd - newOffset);
- regionsBefore[numRegions] = new Region(newOffsetBefore, newEndBefore - newOffsetBefore);
+ regions[numRegions] = new Region(newOffset, newEnd - newOffset);
numRegions++;
}
- if (numRegions < regionsAfter.length) {
- regionsAfter = Arrays.copyOf(regionsAfter, numRegions);
- regionsBefore = Arrays.copyOf(regionsBefore, numRegions);
+ if (numRegions < regions.length) {
+ regions = Arrays.copyOf(regions, numRegions);
}
// Calculate formatting changes for the regions after the refactoring changes.
@@ -374,23 +400,72 @@ public class ChangeGenerator extends ASTVisitor {
CodeFormatter formatter = ToolFactory.createCodeFormatter(options);
code = document.get();
TextEdit[] formatEdits = formatter.format(CodeFormatter.K_TRANSLATION_UNIT, code,
- regionsAfter, TextUtilities.getDefaultLineDelimiter(document));
-
- // For each of the regions we apply formatting changes and create a ReplaceEdit using
- // the region before the refactoring changes and the text after the formatting changes.
- MultiTextEdit resultEdit = new MultiTextEdit();
- for (int i = 0; i < regionsAfter.length; i++) {
- IRegion region = regionsAfter[i];
- int offset = region.getOffset();
- edit = formatEdits[i];
- edit.moveTree(-offset);
- document = new Document(code.substring(offset, offset + region.getLength()));
- edit.apply(document, TextEdit.NONE);
- region = regionsBefore[i];
- edit = new ReplaceEdit(region.getOffset(), region.getLength(), document.get());
- resultEdit.addChild(edit);
+ regions, TextUtilities.getDefaultLineDelimiter(document));
+
+ TextEdit combinedFormatEdit = new MultiTextEdit();
+ for (TextEdit formatEdit : formatEdits) {
+ combinedFormatEdit = TextEditUtil.merge(combinedFormatEdit, formatEdit);
}
- rootEdit = resultEdit;
+ formatEdits = TextEditUtil.flatten(combinedFormatEdit).removeChildren();
+
+ MultiTextEdit result = new MultiTextEdit();
+ int delta = 0;
+ TextEdit edit1 = null;
+ TextEdit edit2 = null;
+ int i = 0;
+ int j = 0;
+ while (true) {
+ if (edit1 == null && i < edits.length)
+ edit1 = edits[i++];
+ if (edit2 == null && j < formatEdits.length)
+ edit2 = formatEdits[j++];
+ if (edit1 == null) {
+ if (edit2 == null)
+ break;
+ edit2.moveTree(-delta);
+ result.addChild(edit2);
+ edit2 = null;
+ } else {
+ int d = TextEditUtil.delta(edit1);
+ if (edit2 == null) {
+ delta += d;
+ result.addChild(edit1);
+ edit1 = null;
+ } else {
+ if (edit2.getExclusiveEnd() - delta <= edit1.getOffset()) {
+ edit2.moveTree(-delta);
+ result.addChild(edit2);
+ edit2 = null;
+ } else {
+ TextEdit piece = clippedEdit(edit2, new Region(-1, edit1.getOffset() + delta));
+ if (piece != null) {
+ piece.moveTree(-delta);
+ result.addChild(piece);
+ }
+ Region region = new Region(edit1.getOffset() + delta, edit1.getLength() + d);
+ int end = endOffset(region);
+ MultiTextEdit format = new MultiTextEdit();
+ while ((piece = clippedEdit(edit2, region)) != null) {
+ format.addChild(piece);
+ if (edit2.getExclusiveEnd() >= end || j >= formatEdits.length) {
+ break;
+ }
+ edit2 = formatEdits[j++];
+ }
+ if (format.hasChildren()) {
+ format.moveTree(-delta);
+ edit1 = applyEdit(format, edit1);
+ }
+ delta += d;
+ result.addChild(edit1);
+ edit1 = null;
+
+ edit2 = clippedEdit(edit2, new Region(end, Integer.MAX_VALUE - end));
+ }
+ }
+ }
+ }
+ rootEdit = result;
} catch (MalformedTreeException e) {
CCorePlugin.log(e);
} catch (BadLocationException e) {
@@ -398,10 +473,39 @@ public class ChangeGenerator extends ASTVisitor {
}
}
+ /**
+ * Applies source edit to the target one and returns the combined edit.
+ */
+ private TextEdit applyEdit(TextEdit source, TextEdit target)
+ throws MalformedTreeException, BadLocationException {
+ source.moveTree(-target.getOffset());
+ String text;
+ if (target instanceof InsertEdit) {
+ text = ((InsertEdit) target).getText();
+ } else if (target instanceof ReplaceEdit) {
+ text = ((ReplaceEdit) target).getText();
+ } else {
+ text = ""; //$NON-NLS-1$
+ }
+
+ IDocument document = new Document(text);
+ source.apply(document, TextEdit.NONE);
+ text = document.get();
+ if (target.getLength() == 0) {
+ return new InsertEdit(target.getOffset(), text);
+ } else {
+ return new ReplaceEdit(target.getOffset(), target.getLength(), text);
+ }
+ }
+
private int endOffset(IRegion region) {
return region.getOffset() + region.getLength();
}
+ private int endOffset(TextEdit edit) {
+ return edit.getOffset() + edit.getLength();
+ }
+
private int endOffset(IASTFileLocation nodeLocation) {
return nodeLocation.getNodeOffset() + nodeLocation.getNodeLength();
}
@@ -451,21 +555,23 @@ public class ChangeGenerator extends ASTVisitor {
insertPos = skipPrecedingBlankLines(tuCode, insertPos);
length -= insertPos;
}
- String code = writer.toString();
- ReplaceEdit edit = new ReplaceEdit(insertPos, length, code);
addToRootEdit(anchorNode);
- rootEdit.addChild(edit);
- processedOffset = edit.getOffset();
+ String code = writer.toString();
+ if (!code.isEmpty())
+ addChildEdit(new InsertEdit(insertPos, code));
+ if (length != 0)
+ addChildEdit(new DeleteEdit(insertPos, length));
}
private void handleReplace(IASTNode node) {
List<ASTModification> modifications = getModifications(node, ModificationKind.REPLACE);
String source = node.getTranslationUnit().getRawSignature();
- TextEdit edit;
ChangeGeneratorWriterVisitor writer =
new ChangeGeneratorWriterVisitor(modificationStore, commentMap);
IASTFileLocation fileLocation = node.getFileLocation();
+ addToRootEdit(node);
if (modifications.size() == 1 && modifications.get(0).getNewNode() == null) {
+ // There is no replacement. We are deleting a piece of existing code.
int offset = getOffsetIncludingComments(node);
int endOffset = getEndOffsetIncludingComments(node);
offset = Math.max(skipPrecedingBlankLines(source, offset), processedOffset);
@@ -492,25 +598,27 @@ public class ChangeGenerator extends ASTVisitor {
writer.newLine();
}
String code = writer.toString();
- edit = new ReplaceEdit(offset, endOffset - offset, code);
+ if (endOffset > offset)
+ addChildEdit(new DeleteEdit(offset, endOffset - offset));
+ if (!code.isEmpty())
+ addChildEdit(new InsertEdit(endOffset, code));
} else {
node.accept(writer);
String code = writer.toString();
int offset = fileLocation.getNodeOffset();
int endOffset = offset + fileLocation.getNodeLength();
- if (node instanceof IASTStatement || node instanceof IASTDeclaration) {
- // Include trailing comments in the area to be replaced.
- endOffset = Math.max(endOffset, getEndOffsetIncludingTrailingComments(node));
- }
String lineSeparator = writer.getScribe().getLineSeparator();
if (code.endsWith(lineSeparator)) {
code = code.substring(0, code.length() - lineSeparator.length());
}
- edit = new ReplaceEdit(offset, endOffset - offset, code);
+ addChildEdit(new ReplaceEdit(offset, endOffset - offset, code));
+ if (node instanceof IASTStatement || node instanceof IASTDeclaration) {
+ // Include trailing comments in the area to be replaced.
+ int commentEnd = getEndOffsetIncludingTrailingComments(node);
+ if (commentEnd > endOffset)
+ addChildEdit(new DeleteEdit(endOffset, commentEnd - endOffset));
+ }
}
- addToRootEdit(node);
- rootEdit.addChild(edit);
- processedOffset = edit.getExclusiveEnd();
}
private void handleAppends(IASTNode node) {
@@ -537,13 +645,12 @@ public class ChangeGenerator extends ASTVisitor {
if (node instanceof ICPPASTNamespaceDefinition) {
writer.newLine();
}
- String code = writer.toString();
addToRootEdit(node);
- ReplaceEdit edit = new ReplaceEdit(anchor.getOffset(), anchor.getLength(),
- code + anchor.getText());
- rootEdit.addChild(edit);
- IASTFileLocation fileLocation = node.getFileLocation();
- processedOffset = endOffset(fileLocation);
+ String code = writer.toString();
+ if (!code.isEmpty())
+ addChildEdit(new InsertEdit(anchor.getOffset(), code));
+ addChildEdit(new ReplaceEdit(anchor.getOffset(), anchor.getLength(), anchor.getText()));
+ processedOffset = endOffset(node);
}
private void handleAppends(IASTTranslationUnit node) {
@@ -565,6 +672,7 @@ public class ChangeGenerator extends ASTVisitor {
String source = node.getRawSignature();
int endOffset = skipTrailingBlankLines(source, offset);
+ addToRootEdit(node);
ChangeGeneratorWriterVisitor writer =
new ChangeGeneratorWriterVisitor(modificationStore, commentMap);
IASTNode newNode = null;
@@ -593,8 +701,10 @@ public class ChangeGenerator extends ASTVisitor {
}
String code = writer.toString();
- addToRootEdit(node);
- rootEdit.addChild(new ReplaceEdit(offset, endOffset - offset, code));
+ if (!code.isEmpty())
+ addChildEdit(new InsertEdit(offset, code));
+ if (endOffset > offset)
+ addChildEdit(new DeleteEdit(offset, endOffset - offset));
}
/**
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/TextEditUtil.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/TextEditUtil.java
new file mode 100644
index 0000000000..3b5b2b6f5b
--- /dev/null
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/TextEditUtil.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.core.dom.rewrite.changegenerator;
+
+import org.eclipse.text.edits.CopyTargetEdit;
+import org.eclipse.text.edits.DeleteEdit;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.MoveSourceEdit;
+import org.eclipse.text.edits.MoveTargetEdit;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+
+public class TextEditUtil {
+ // Do not instantiate. All methods are static.
+ private TextEditUtil() {
+ }
+
+ /**
+ * Degenerates the given edit tree into a list.<br>
+ * All nodes of the result are leafs.<br>
+ * <strong>The given edit is modified and can no longer be used.</strong>
+ *
+ * @param edit the edit tree to flatten
+ * @return a MultiTextEdit containing the list of edits
+ */
+ public static MultiTextEdit flatten(TextEdit edit) {
+ MultiTextEdit result= new MultiTextEdit();
+ flatten(edit, result);
+ return result;
+ }
+
+ private static void flatten(TextEdit edit, MultiTextEdit result) {
+ if (edit.hasChildren()) {
+ TextEdit[] children= edit.getChildren();
+ for (int i= 0; i < children.length; i++) {
+ TextEdit child= children[i];
+ child.getParent().removeChild(0);
+ flatten(child, result);
+ }
+ } else if (!(edit instanceof MultiTextEdit)) {
+ result.addChild(edit);
+ }
+ }
+
+ /**
+ * Create an edit which contains <code>edit1</code> and <code>edit2</code>
+ * <p><strong>The given edits are modified and they can no longer be used.</strong></p>
+ *
+ * @param edit1 the edit to merge with edit2
+ * @param edit2 the edit to merge with edit1
+ * @return the merged tree
+ * @throws MalformedTreeException if the two edits ovelap
+ */
+ public static TextEdit merge(TextEdit edit1, TextEdit edit2) {
+ if (edit1 instanceof MultiTextEdit && !edit1.hasChildren()) {
+ return edit2;
+ }
+
+ if (edit2 instanceof MultiTextEdit && !edit2.hasChildren()) {
+ return edit1;
+ }
+
+ MultiTextEdit result= new MultiTextEdit();
+ merge(edit1, edit2, result);
+ return result;
+ }
+
+ private static void merge(TextEdit edit1, TextEdit edit2, MultiTextEdit result) {
+ if (edit1 instanceof MultiTextEdit && edit2 instanceof MultiTextEdit) {
+ MultiTextEdit multiTextEdit1= (MultiTextEdit) edit1;
+ if (!multiTextEdit1.hasChildren()) {
+ result.addChild(edit2);
+ return;
+ }
+
+ MultiTextEdit multiTextEdit2= (MultiTextEdit) edit2;
+ if (!multiTextEdit2.hasChildren()) {
+ result.addChild(edit1);
+ return;
+ }
+
+ TextEdit[] children1= multiTextEdit1.getChildren();
+ TextEdit[] children2= multiTextEdit2.getChildren();
+
+ int i1= 0;
+ int i2= 0;
+ while (i1 < children1.length && i2 < children2.length) {
+ while (i1 < children1.length && children1[i1].getExclusiveEnd() < children2[i2].getOffset()) {
+ edit1.removeChild(0);
+ result.addChild(children1[i1]);
+ i1++;
+ }
+ if (i1 >= children1.length)
+ break;
+
+ while (i2 < children2.length && children2[i2].getExclusiveEnd() < children1[i1].getOffset()) {
+ edit2.removeChild(0);
+ result.addChild(children2[i2]);
+ i2++;
+ }
+ if (i2 >= children2.length)
+ break;
+
+ if (children1[i1].getExclusiveEnd() < children2[i2].getOffset())
+ continue;
+
+ edit1.removeChild(0);
+ edit2.removeChild(0);
+ merge(children1[i1], children2[i2], result);
+
+ i1++;
+ i2++;
+ }
+
+ while (i1 < children1.length) {
+ edit1.removeChild(0);
+ result.addChild(children1[i1]);
+ i1++;
+ }
+
+ while (i2 < children2.length) {
+ edit2.removeChild(0);
+ result.addChild(children2[i2]);
+ i2++;
+ }
+ } else if (edit1 instanceof MultiTextEdit) {
+ TextEdit[] children= edit1.getChildren();
+
+ int i= 0;
+ while (children[i].getExclusiveEnd() < edit2.getOffset()) {
+ edit1.removeChild(0);
+ result.addChild(children[i]);
+ i++;
+ if (i >= children.length) {
+ result.addChild(edit2);
+ return;
+ }
+ }
+ edit1.removeChild(0);
+ merge(children[i], edit2, result);
+ i++;
+ while (i < children.length) {
+ edit1.removeChild(0);
+ result.addChild(children[i]);
+ i++;
+ }
+ } else if (edit2 instanceof MultiTextEdit) {
+ TextEdit[] children= edit2.getChildren();
+
+ int i= 0;
+ while (children[i].getExclusiveEnd() < edit1.getOffset()) {
+ edit2.removeChild(0);
+ result.addChild(children[i]);
+ i++;
+ if (i >= children.length) {
+ result.addChild(edit1);
+ return;
+ }
+ }
+ edit2.removeChild(0);
+ merge(edit1, children[i], result);
+ i++;
+ while (i < children.length) {
+ edit2.removeChild(0);
+ result.addChild(children[i]);
+ i++;
+ }
+ } else {
+ if (edit1.getExclusiveEnd() < edit2.getOffset()) {
+ result.addChild(edit1);
+ result.addChild(edit2);
+ } else {
+ result.addChild(edit2);
+ result.addChild(edit1);
+ }
+ }
+ }
+
+ /**
+ * Returns the difference in the document length caused by the edit. {@code InsertEdit}s have
+ * positive delta, {@code DeleteEdit}s have negative one.
+ * @param edit the edit to determine delta for.
+ * @return the delta
+ */
+ public static int delta(TextEdit edit) {
+ int delta = 0;
+ for (TextEdit child : edit.getChildren()) {
+ delta += delta(child);
+ }
+ delta += ownDelta(edit);
+ return delta;
+ }
+
+ private static int ownDelta(TextEdit edit) {
+ if (edit instanceof DeleteEdit || edit instanceof MoveSourceEdit) {
+ return -edit.getLength();
+ } else if (edit instanceof InsertEdit) {
+ return ((InsertEdit) edit).getText().length();
+ } else if (edit instanceof ReplaceEdit) {
+ return ((ReplaceEdit) edit).getText().length() - edit.getLength();
+ } else if (edit instanceof CopyTargetEdit) {
+ return ((CopyTargetEdit) edit).getSourceEdit().getLength();
+ } else if (edit instanceof MoveTargetEdit) {
+ return ((MoveTargetEdit) edit).getSourceEdit().getLength();
+ }
+ return 0;
+ }
+}

Back to the top