### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: formatter/org/eclipse/jdt/internal/formatter/OptimizedReplaceEdit.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/OptimizedReplaceEdit.java,v retrieving revision 1.10 diff -u -r1.10 OptimizedReplaceEdit.java --- formatter/org/eclipse/jdt/internal/formatter/OptimizedReplaceEdit.java 17 Aug 2010 10:16:57 -0000 1.10 +++ formatter/org/eclipse/jdt/internal/formatter/OptimizedReplaceEdit.java 2 Sep 2010 07:52:16 -0000 @@ -23,6 +23,6 @@ } public String toString() { - return "(" + this.offset + ", length " + this.length + " :>" + this.replacement + "<"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ + return (this.offset < 0 ? "(" : "X(") + this.offset + ", length " + this.length + " :>" + this.replacement + "<"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ //$NON-NLS-5$ } } Index: formatter/org/eclipse/jdt/internal/formatter/Scribe.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Scribe.java,v retrieving revision 1.217 diff -u -r1.217 Scribe.java --- formatter/org/eclipse/jdt/internal/formatter/Scribe.java 31 Aug 2010 07:03:07 -0000 1.217 +++ formatter/org/eclipse/jdt/internal/formatter/Scribe.java 2 Sep 2010 07:52:18 -0000 @@ -220,26 +220,12 @@ // the offset of the region is inside a comment => restart the region from the comment start adaptedOffset = this.commentPositions[index][0]; if (adaptedOffset >= 0) { - // adapt only javadoc or block commments. Since fix for bug + // adapt only javadoc or block comments. Since fix for bug // https://bugs.eclipse.org/bugs/show_bug.cgi?id=238210 // edits in line comments only concerns whitespaces hence can be // treated as edits in code adaptedLength = length + offset - adaptedOffset; commentIndex = index; - // include also the indentation edit just before the comment if any - for (int j=0; j 0 && this.edits[j].replacement.trim().length() == 0) { - adaptedLength += adaptedOffset - this.edits[j].offset; - adaptedOffset = editOffset; - break; - } - } else if (editEnd > adaptedOffset) { - break; - } - } } } index = getCommentIndex(commentIndex, offset+length-1); @@ -300,6 +286,14 @@ currentEdit = index; } } + + // Set invalid all edits outside the region + if (currentEdit != -1) { + int length = sortedEdits.length; + for (int e=currentEdit; e no possible overlap of region's start + if (editStart > regionStart) { // the edit starts after the region's start => no possible overlap of region's start top = i-1; - if (regionEnd < editStart) { // the edit starts after the region's end => no possible overlap of region's end + if (editStart > regionEnd) { // the edit starts after the region's end => no possible overlap of region's end topEnd = top; } } else { - if (regionStart >= editEnd) { // the edit ends before the region's start => no possible overlap of region's start + if (editEnd < regionStart) { // the edit ends before the region's start => no possible overlap of region's start bottom = i+1; } else { // Count the lines of the edit which are outside the region - linesOutside = 0; + int linesOutside = 0; + StringBuffer spacesOutside = new StringBuffer(); this.scanner.resetTo(editStart, editEnd-1); - while (!this.scanner.atEnd()) { - boolean before = this.scanner.currentPosition < regionStart; - char ch = (char) this.scanner.getNextChar(); - if (ch == '\n' ) { - if (before) linesOutside++; - } - } + while (this.scanner.currentPosition < regionStart && !this.scanner.atEnd()) { + char ch = (char) this.scanner.getNextChar(); + switch (ch) { + case '\n': + linesOutside++; + spacesOutside.setLength(0); + break; + case '\r': + break; + default: + spacesOutside.append(ch); + break; + } + } // Restart the edit at the beginning of the line where the region start edit.offset = regionStart; + int editLength = edit.length; edit.length -= edit.offset - editStart; // Cut replacement string if necessary @@ -364,36 +367,64 @@ if (edit.replacement.charAt(idx) == '\n') linesReplaced++; } - // As the edit starts outside the region, remove first lines from edit string if any - if (linesReplaced > 0) { - int linesCount = linesOutside >= linesReplaced ? linesReplaced : linesOutside; - if (linesCount > 0) { - int idx=0; - loop: while (idx < length) { - char ch = edit.replacement.charAt(idx); - switch (ch) { - case '\n': - linesCount--; - if (linesCount == 0) { - idx++; - break loop; - } - break; - case '\r': - case ' ': - case '\t': - break; - default: - break loop; - } - idx++; - } - if (idx >= length) { - edit.replacement = ""; //$NON-NLS-1$ - } else { - edit.replacement = edit.replacement.substring(idx); - } - } + // If the edit was a replacement but become an insertion due to the length reduction + // and if the edit finishes just before the region starts and if there's no line to replace + // then there's no replacement to do... + if (editLength > 0 && edit.length == 0 && editEnd == regionStart && linesReplaced == 0 && linesOutside== 0) { + edit.offset = -1; + } else { + + // As the edit starts outside the region, remove first lines from edit string if any + if (linesReplaced > 0) { + int linesCount = linesOutside >= linesReplaced ? linesReplaced : linesOutside; + if (linesCount > 0) { + int idx = 0; + loop: while (idx < length) { + char ch = edit.replacement.charAt(idx); + switch (ch) { + case '\n': + linesCount--; + if (linesCount == 0) { + idx++; + break loop; + } + break; + case '\r': + case ' ': + case '\t': + break; + default: + break loop; + } + idx++; + } + // Compare spaces outside the region and the beginning + // of the replacement string to remove the common part + int spacesOutsideLength = spacesOutside.length(); + int replacementStart = idx; + for (int o=0, r=0; o < spacesOutsideLength && r<(length-idx); o++) { + char rch = edit.replacement.charAt(idx + r); + char och = spacesOutside.charAt(o); + if (rch == och) { + replacementStart++; + r++; + } else if (rch == '\t' && (this.tabLength > 0 && och == ' ')) { + if ((o+1)%this.tabLength == 0) { + replacementStart++; + r++; + } + } else { + break; + } + } + // Update the replacement string + if (replacementStart >= length) { + edit.offset = -1; + } else { + edit.replacement = edit.replacement.substring(replacementStart); + } + } + } } } overlapIndex = i; @@ -401,6 +432,7 @@ } } } + int validIndex = (overlapIndex != -1) ? overlapIndex : bottom; // Look for an edit overlapping the region end if (overlapIndex != -1) bottom = overlapIndex; @@ -409,53 +441,87 @@ edit = sortedEdits[i]; int editStart = edit.offset; int editEnd = editStart + edit.length; - if (regionEnd < editStart) { // the edit starts after the region's end => no possible overlap of region's end + if (regionEnd < editStart) { // the edit starts after the region's end => no possible overlap of region's end topEnd = i-1; - } else { - if (regionEnd >= editEnd) { // the edit ends before the region's end => no possible overlap of region's end - bottom = i+1; - } else { - // Count the lines of the edit which are outside the region - linesOutside = 0; - this.scanner.resetTo(editStart, editEnd-1); - while (!this.scanner.atEnd()) { - boolean after = this.scanner.currentPosition >= regionEnd; - char ch = (char) this.scanner.getNextChar(); - if (ch == '\n' ) { - if (after) linesOutside++; - } - } - - // Cut replacement string if necessary - int length = edit.replacement.length(); - if (length > 0) { + } else if (regionEnd == editStart) { // special case when the edit starts just after the region's end... + // ...we got the last index of the edit inside the region + topEnd = i - 1; + // this last edit is valid only if it's an insertion and if it has indentation + if (edit.length == 0) { + int nrLength = 0; + int rLength = edit.replacement.length(); + int ch = edit.replacement.charAt(nrLength); + loop: while (nrLength < rLength) { + switch (ch) { + case ' ': + case '\t': + nrLength++; + break; + default: + break loop; + } + } + if (nrLength > 0) { + topEnd++; + if (nrLength < rLength) { + edit.replacement = edit.replacement.substring(0, nrLength); + } + } + } + break; + } else if (editEnd <= regionEnd) { // the edit ends before the region's end => no possible overlap of region's end + bottom = i+1; + } else { + // Count the lines of the edit which are outside the region + int linesOutside = 0; + this.scanner.resetTo(editStart, editEnd-1); + while (!this.scanner.atEnd()) { + boolean after = this.scanner.currentPosition >= regionEnd; + char ch = (char) this.scanner.getNextChar(); + if (ch == '\n' ) { + if (after) linesOutside++; + } + } - // Count the lines in replacement string - int linesReplaced = 0; - for (int idx=0; idx < length; idx++) { - if (edit.replacement.charAt(idx) == '\n') linesReplaced++; - } + // Cut replacement string if necessary + int length = edit.replacement.length(); + if (length > 0) { + + // Count the lines in replacement string + int linesReplaced = 0; + for (int idx=0; idx < length; idx++) { + if (edit.replacement.charAt(idx) == '\n') linesReplaced++; + } - // Set the replacement string to the number of missing new lines - // As the end of the edit is out of the region, the possible trailing - // indentation should not be added... - if (linesReplaced == 0) { + // Set the replacement string to the number of missing new lines + // As the end of the edit is out of the region, the possible trailing + // indentation should not be added... + if (linesReplaced == 0) { + edit.replacement = ""; //$NON-NLS-1$ + } else { + int linesCount = linesReplaced > linesOutside ? linesReplaced - linesOutside : 0; + if (linesCount == 0) { edit.replacement = ""; //$NON-NLS-1$ } else { - int linesCount = linesReplaced > linesOutside ? linesReplaced - linesOutside : 0; - if (linesCount == 0) { - edit.replacement = ""; //$NON-NLS-1$ - } else { - edit.replacement = getNewLineString(linesCount); - } + edit.replacement = getNewLineString(linesCount); } } - edit.length -= editEnd - regionEnd; - return i; } + edit.length = regionEnd - editStart; + + // We got the last edit of the regions, give up + topEnd = i; + break; } } - return overlapIndex; + + // Set invalid all edits outside the region + for (int e=initialStart; e 0) { return this.edits[this.editsIndex - 1]; @@ -1205,15 +1235,6 @@ return getNewLine(); } - private IRegion getAdaptedRegionAt(int offset) { - int index = getIndexOfAdaptedRegionAt(offset); - if (index < 0) { - return null; - } - - return this.adaptedRegions[index]; - } - public TextEdit getRootEdit() { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=208541 adaptRegions(); @@ -1246,14 +1267,16 @@ } for (int i= 0, max = this.editsIndex; i < max; i++) { OptimizedReplaceEdit currentEdit = this.edits[i]; - if (isValidEdit(currentEdit)) { - try { - edit.addChild(new ReplaceEdit(currentEdit.offset, currentEdit.length, currentEdit.replacement)); - } - catch (MalformedTreeException ex) { - // log exception in case of error - CommentFormatterUtil.log(ex); - throw ex; + if (currentEdit.offset >= 0 && currentEdit.offset <= this.scannerEndPosition) { + if (currentEdit.length == 0 || (currentEdit.offset != this.scannerEndPosition && isMeaningfulEdit(currentEdit))) { + try { + edit.addChild(new ReplaceEdit(currentEdit.offset, currentEdit.length, currentEdit.replacement)); + } + catch (MalformedTreeException ex) { + // log exception in case of error + CommentFormatterUtil.log(ex); + throw ex; + } } } } @@ -1475,71 +1498,19 @@ return previousLineEnd != -1 && previousLineEnd == start - 1; } - private boolean isValidEdit(OptimizedReplaceEdit edit) { + private boolean isMeaningfulEdit(OptimizedReplaceEdit edit) { final int editLength= edit.length; final int editReplacementLength= edit.replacement.length(); final int editOffset= edit.offset; - if (editLength != 0) { - - IRegion covering = getCoveringAdaptedRegion(editOffset, (editOffset + editLength - 1)); - if (covering != null) { - if (editReplacementLength != 0 && editLength == editReplacementLength) { - for (int i = editOffset, max = editOffset + editLength; i < max; i++) { - if (this.scanner.source[i] != edit.replacement.charAt(i - editOffset)) { - return true; - } - } - return false; - } - return true; - } - - IRegion starting = getAdaptedRegionAt(editOffset + editLength); - if (starting != null) { - int i = editOffset; - for (int max = editOffset + editLength; i < max; i++) { - int replacementStringIndex = i - editOffset; - if (replacementStringIndex >= editReplacementLength || this.scanner.source[i] != edit.replacement.charAt(replacementStringIndex)) { - break; - } - } - if (i - editOffset != editReplacementLength && i != editOffset + editLength - 1) { - edit.offset = starting.getOffset(); - edit.length = 0; - edit.replacement = edit.replacement.substring(i - editOffset); + if (editReplacementLength != 0 && editLength == editReplacementLength) { + for (int i = editOffset, max = editOffset + editLength; i < max; i++) { + if (this.scanner.source[i] != edit.replacement.charAt(i - editOffset)) { return true; } } - return false; } - - IRegion covering = getCoveringAdaptedRegion(editOffset, editOffset); - if (covering != null) { - return true; - } - - if (editOffset == this.scannerEndPosition) { - int index = Arrays.binarySearch( - this.adaptedRegions, - new Region(editOffset, 0), - new Comparator() { - public int compare(Object o1, Object o2) { - IRegion r1 = (IRegion)o1; - IRegion r2 = (IRegion)o2; - - int r1End = r1.getOffset() + r1.getLength(); - int r2End = r2.getOffset() + r2.getLength(); - - return r1End - r2End; - } - }); - if (index < 0) { - return false; - } - return true; - } - return false; + return true; } private void preserveEmptyLines(int count, int insertPosition) { @@ -3960,7 +3931,9 @@ // Replace the new line with a single space when there's only one separator // or, if necessary, print the indentation on the last line if (!multiLinesBlock) { - addReplaceEdit(firstLineEnd, end, " "); //$NON-NLS-1$ + if (firstLineEnd > 0) { + addReplaceEdit(firstLineEnd, end, " "); //$NON-NLS-1$ + } } else if (secondLineStart > 0) { if (newLineString == null) { #P org.eclipse.jdt.core.tests.model Index: src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java,v retrieving revision 1.36 diff -u -r1.36 FormatterBugsTests.java --- src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java 31 Aug 2010 07:03:10 -0000 1.36 +++ src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java 2 Sep 2010 07:52:22 -0000 @@ -1690,7 +1690,6 @@ * @bug 252556: [formatter] Spaces removed before formatted region of a compilation unit. * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=252556" */ -// TODO Fix the bug... this test currently verifies that the problem still occurs! public void testBug252556() { String source = "package a;\n" + @@ -1711,8 +1710,7 @@ "public class Test {\n" + "\n" + " private int field;\n" + -// " \n" + // this is the expected untouched line - "\n" + // instead the tab is removed although it is outside the selection... + " \n" + " /**\n" + " * fds\n" + " */\n" + @@ -1721,6 +1719,161 @@ "}\n" ); } +// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=95340 +public void testBug252556a() { + String source = + "public class Test {\n" + + "\n" + + "int foo() {[#\n" + + "return 0;\n" + + "#]}\n" + + "void bar(){}\n" + + "}\n"; + formatSource(source, + "public class Test {\n" + + "\n" + + "int foo() {\n" + + " return 0;\n" + + " }\n" + + "void bar(){}\n" + + "}\n" + ); +} +// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=95340 +public void testBug252556b() { + String source = + "public class Test {\n" + + "\n" + + "int [#foo() {\n" + + "return 0;\n" + + "#]}\n" + + "void bar(){}\n" + + "}\n"; + formatSource(source, + "public class Test {\n" + + "\n" + + "int foo() {\n" + + " return 0;\n" + + " }\n" + + "void bar(){}\n" + + "}\n" + ); +} +// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=95340 +public void testBug252556c() { + String source = + "public class Test {\n" + + "\n" + + "[#int foo() {\n" + + "return 0;\n" + + "#]}\n" + + "void bar(){}\n" + + "}\n"; + formatSource(source, + "public class Test {\n" + + "\n" + + " int foo() {\n" + + " return 0;\n" + + " }\n" + + "void bar(){}\n" + + "}\n" + ); +} +// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=95340 +public void testBug252556d() { + String source = + "public class Test {\n" + + "\n" + + "[#int foo() {\n" + + "return 0;\n" + + "}#]\n" + + "void bar(){}\n" + + "}\n"; + formatSource(source, + "public class Test {\n" + + "\n" + + " int foo() {\n" + + " return 0;\n" + + " }\n" + + "void bar(){}\n" + + "}\n" + ); +} +// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=95340 +public void testBug252556e() { + String source = + "public class Test {\n" + + "\n" + + "[#int foo() {\n" + + "return 0;\n" + + "}\n" + + "#]void bar(){}\n" + + "}\n"; + formatSource(source, + "public class Test {\n" + + "\n" + + " int foo() {\n" + + " return 0;\n" + + " }\n" + + "\n" + + " void bar(){}\n" + + "}\n" + ); +} +// see org.eclipse.jdt.ui.tests.core.CodeFormatterUtilTest.testFormatSubstring() +public void testBug252556f() { + String source = + "package test1;\n" + + "\n" + + "import java.util.Vector;\n" + + "\n" + + "public class A {\n" + + " public void foo() {\n" + + " [#Runnable runnable= new Runnable() {};#]\n" + + " runnable.toString();\n" + + " }\n" + + "}\n"; + formatSource(source, + "package test1;\n" + + "\n" + + "import java.util.Vector;\n" + + "\n" + + "public class A {\n" + + " public void foo() {\n" + + " Runnable runnable = new Runnable() {\n" + + " };\n" + + " runnable.toString();\n" + + " }\n" + + "}\n" + ); +} +// Adding a test case impacted by the fix for bug 252556 got from massive tests +public void testBug252556_wksp3a() { + String source = + "package wksp3;\n" + + "\n" + + "/**\n" + + " *
import java.net.*;\n" + 
+		" * import org.xml.sax.*;\n" + 
+		" * 
\n" + + " */\n" + + "public class X01 {\n" + + "\n" + + "}\n"; + formatSource(source, + "package wksp3;\n" + + "\n" + + "/**\n" + + " *
\n" + 
+		" * import java.net.*;\n" + 
+		" * import org.xml.sax.*;\n" + 
+		" * 
\n" + + " */\n" + + "public class X01 {\n" + + "\n" + + "}\n" + ); +} /** * @bug 281655: [formatter] "Never join lines" does not work for annotations. Index: src/org/eclipse/jdt/core/tests/formatter/FormatterCommentsBugsTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterCommentsBugsTest.java,v retrieving revision 1.71 diff -u -r1.71 FormatterCommentsBugsTest.java --- src/org/eclipse/jdt/core/tests/formatter/FormatterCommentsBugsTest.java 17 Aug 2010 10:17:00 -0000 1.71 +++ src/org/eclipse/jdt/core/tests/formatter/FormatterCommentsBugsTest.java 2 Sep 2010 07:52:24 -0000 @@ -714,7 +714,7 @@ "public class Test {\r\n" + "\r\n" + " private int field;\r\n" + - "\r\n" + + " \r\n" + " /**\r\n" + " * fds\r\n" + " */\r\n" + @@ -954,8 +954,8 @@ // This is due to the fact that the region is adapted to include the edit just before the comment formatSource(source, "public class C {\n" + - "\n" + - " /**\n" + + " \n" + + " /**\n" + " * a b c d .\n" + " */\n" + " void m1() {\n" + @@ -987,8 +987,8 @@ // This is due to the fact that the region is adapted to include the edit just before the comment formatSource(source, "public class C {\n" + - "\n" + - " /**\n" + + " \n" + + " /**\n" + " * a b c d .\n" + " */\n" + " void m1 ( ) {\n" + @@ -1083,9 +1083,9 @@ // Note that the incorrect indentation before the javadoc is fixed in this test case... // This is due to the fact that the region is adapted to include the edit just before the comment formatSource(source, - " public class C{\n" + - "\n" + - " /**\n" + + " public class C{ \n" + + " \n" + + " /**\n" + " * a b c d .\n" + " */\n" + " void m1 ( ) {\n" + @@ -1147,8 +1147,8 @@ // This is due to the fact that the region is adapted to include the edit just before the comment formatSource(source, "public class D {\n" + - "\n" + - " /*\n" + + " \n" + + " /*\n" + " * a b c d .\n" + " */\n" + " void m2() {\n" + @@ -1180,8 +1180,8 @@ // This is due to the fact that the region is adapted to include the edit just before the comment formatSource(source, "public class D {\n" + - "\n" + - " /*\n" + + " \n" + + " /*\n" + " * a b c d .\n" + " */\n" + " void m2 ( ) {\n" + @@ -1276,9 +1276,9 @@ // Note that the incorrect indentation before the javadoc is fixed in this test case... // This is due to the fact that the region is adapted to include the edit just before the comment formatSource(source, - " public class D{\n" + - "\n" + - " /*\n" + + " public class D{ \n" + + " \n" + + " /*\n" + " * a b c d .\n" + " */\n" + " void m2 ( ) {\n" + @@ -1553,7 +1553,7 @@ formatSource(source, "\n" + "public class E01 {\n" + - " /**\n" + + " /**\n" + " * Javadoc comment\n" + " */\n" + " /*\n" +