Bug 5471 - CodeFormatter mapped positions broken for multi-line comments
Summary: CodeFormatter mapped positions broken for multi-line comments
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 2.0   Edit
Hardware: All All
: P2 normal (vote)
Target Milestone: 2.0 M1   Edit
Assignee: Olivier Thomann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 4071
  Show dependency tree
 
Reported: 2001-11-02 11:09 EST by Claude Knaus CLA
Modified: 2002-01-11 09:22 EST (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Claude Knaus CLA 2001-11-02 11:09:36 EST
The following code demonstrates two problems with mapped positions:

  1) position at end of source gets mapped to 0
  2) positions are not mapped correctly if the source contains
     multi-line comments.

For 2), it seems that the wrong offset of a position is equal to the number of 
line delimiters in the multi-line comment. I will workaround this by escaping 
multi-line comments with single-line comments, but it would be nice to have 
this fixed.

---8<--- source ---8<---

import org.eclipse.jdt.internal.formatter.CodeFormatter;

public class CodeFormatterBug {

	private static void print(String string, int[] positions) {
		for (int i= 0; i < positions.length; i++)
			System.out.print(positions[i] + " ");			
		System.out.println("");

		for (int i= 0; i < positions.length - 1; i++) {
			System.out.print("#"); // mark position			
			
			System.out.print(string.substring(positions[i], 
positions[i + 1]));
		}
	}

	public static void main(String arguments[]) {
		String string=
			"/**\n" +
			" * foo\n" +
			" */\n" +
			"Foo Bar\n" +
			"blah blah\n";
		
		int[] positions= {
			0,
			string.indexOf("foo"),	
			string.indexOf("Foo"),	
			string.indexOf("Bar"),
			string.length()
		};
	
		CodeFormatter formatter= new CodeFormatter();
		
		System.out.println("before:");
		print(string, positions);
				
		formatter.setPositionsToMap(positions);
		string= formatter.formatSourceString(string);		
		positions= formatter.getMappedPositions();

		System.out.println("after:");
		print(string, positions);		
	}
}

---8<--- output ---8<---

before:
0 7 15 19 33 
#/**
 * #foo
 */
#Foo #Bar
blah blah
after:
0 12 20 24 0 
#/**
 * foo
#
 */
Fo#o Ba#java.lang.StringIndexOutOfBoundsException: String index out of range: -
24
	at java.lang.String.substring(String.java:1504)
	at CodeFormatterBug.print(CodeFormatterBug.java:12)
	at CodeFormatterBug.main(CodeFormatterBug.java:42)
Exception in thread "main"
Comment 1 Claude Knaus CLA 2001-11-09 05:47:18 EST
Additional information:

I need this feature to support multi-line comments in templates. I'd like
to create templates for e.g. public methods:

/**
 *
 */
public ${return_type} ${name}(${arguments}) {
    ${cursor}
}

Right now, I have no good way to catch this situation.
Comment 2 Olivier Thomann CLA 2001-11-13 14:40:22 EST
Fix done in the CodeFormatter class. Several problems were found with the 
multiple lines comments.
All code formatter tests passed.
Comment 3 Claude Knaus CLA 2001-11-14 11:19:34 EST
I was wrong with my verification. The bug is not fixed. I run the program above 
again and got the same result.
Comment 4 Olivier Thomann CLA 2001-11-14 14:11:42 EST
The fix is done only in HEAD. It hasn't been integrated in any build yet. If you 
want me to set it up on your machine let me know. You simply need to replace the 
CodeFormatter file.
Comment 5 Claude Knaus CLA 2001-11-15 03:47:09 EST
Oh, I see. Can you send me the file, or would it be already in the build
20011114?
Comment 6 Olivier Thomann CLA 2001-11-16 09:45:56 EST
Unfortunately it seems that this fix is not included in the 20011115 build 
according to the release note. I will send you the file. It is only one class to 
change. You need to patch  your jdtcore.jar file in your install.
Comment 7 Claude Knaus CLA 2001-11-20 10:25:17 EST
I have problems testing because of another bug. It seems the fixed 
CodeFormatter is still not in the repository. When is it going to be released?
Comment 8 Claude Knaus CLA 2001-11-20 10:40:07 EST
Ok, I can verify that the CodeFormatter is fixed for the test case. Still 
waiting for it to be checked into the repository... :)
Comment 9 Olivier Thomann CLA 2001-11-20 10:55:51 EST
According to the build notes provided for the next integration build (20011120), 
it should be included in this build. It should be available this afternoon 
(Ottawa's time).
Comment 10 Claude Knaus CLA 2001-11-21 12:36:56 EST
Unfortunately, I found another test case, where positions are not mapped 
correctly. The position in front of comment gets mapped into the comment.
Note that if you remove the last line, the mapped position is correct.

---8<---

package foo;

import org.eclipse.jdt.internal.formatter.CodeFormatter;

public class CodeFormatterBug2 {

	private static void print(String string, int[] positions) {
		for (int i= 0; i < positions.length; i++)
			System.out.print(positions[i] + " ");			
		System.out.println("");

		for (int i= 0; i < positions.length - 1; i++) {
			System.out.print("#"); // mark position			
			
			System.out.print(string.substring(positions[i], 
positions[i + 1]));
		}
	}

	public static void main(String arguments[]) {
		String string=
			"foo\n" +
			"/*${cursor}*/\n" +
			"Foo\n"; // bug doesn't occur if you remove this line
		
		int[] positions= {
			0,
			string.indexOf("/"),	
			string.length()
		};
	
		CodeFormatter formatter= new CodeFormatter();
		
		System.out.println("before:");
		print(string, positions);
				
		formatter.setPositionsToMap(positions);
		string= formatter.formatSourceString(string);		
		positions= formatter.getMappedPositions();

		System.out.println("after:");
		print(string, positions);
		
		while(true);
	}
}

---8<--- output ---
before:
0 4 22 
#foo
#/*${cursor}*/
Foo
after:
0 6 23 
#foo
/#*${cursor}*/
Foo
Comment 11 Olivier Thomann CLA 2001-11-21 14:49:48 EST
This is a issue with the line separator. When the line separator (\n) is 
consumed after the foo token it is replaced by \r\n which contains an extra 
character. The offset is actually one and not two. Right now I take two instead 
of one and this explain the offset of one.
I am looking at a way to set properly the offset between formatted source and 
original source. If you change your test case to set the line separator of the 
code formatter to be \n, then the positions are right even if you keep the last 
line.
Comment 12 Claude Knaus CLA 2001-11-22 03:44:20 EST
This problem did not occure befor the other bug fix. Was it some coincidence 
that it happened to work?

I'll use the workaround of specifying the same line delimiter used in the 
string for now.
Comment 13 Olivier Thomann CLA 2001-11-22 10:41:08 EST
Yes, this was a side-effect. But before the formatter could never map properly 
if the line delimiter has 2 characters and the string you want to format 
contained only one character line delimiter and the string contained a multiple 
line comment and it was a coincidence that this code was formatted properly. 
That change had to be done.
I found the problem. I simply forgot to map the positions inside the comment in 
case there is not line delimiters in the comment. I fixed it, but I have trouble 
to run the tests inside my 20011116 workspace. As soon as I can do it and I 
don't find any regression, I release it.
Comment 14 Olivier Thomann CLA 2001-11-22 11:22:33 EST
The fix is released in HEAD. I don't know when it will be integrated in a build.
Comment 15 Claude Knaus CLA 2001-11-29 12:51:20 EST
It's me again :-)

I have a case where the position before a multi-byte line-break ends up
being inside a multi-byte line-break. This has severe consequences when
used as a selection in a StyledText. Although I think StyledText should
not choke on selection inside multi-byte line-breaks, the CodeFormatter
should not produce these off-by-one positions... So far, this only
happened within a multi-line comment.

---8>---

package foo;

import org.eclipse.jdt.internal.formatter.CodeFormatter;

public class CodeFormatterBug3 {

	private static void print(String string, int[] positions) {
		for (int i= 0; i < positions.length; i++)
			System.out.print(positions[i] + " ");			
		System.out.println("");

		for (int i= 0; i < positions.length - 1; i++) {
			System.out.print("#"); // mark position			
			
			System.out.print(string.substring(positions[i], 
positions[i + 1]));
		}
	}

	public static void main(String arguments[]) {
		String string=
			"foo\r\n" +
			"/**X\r\n" +
			" * foo\r\n" +
			" */\r\n" +
			"bar";
		
		int[] positions= {
			0,
			string.indexOf("X") + 1,	
			string.length()
		};
	
		CodeFormatter formatter= new CodeFormatter();
		
		System.out.println("before:");
		print(string, positions);
				
		formatter.setPositionsToMap(positions);
		string= formatter.formatSourceString(string);		
		positions= formatter.getMappedPositions();

		System.out.println("after:");
		print(string, positions);
	}
}

---8<---output---

before:
0 9 27 
#foo
/**X#
 * foo
 */
barafter:
0 10 27 
#foo
/**X
#
 * foo
 */
bar
Comment 16 Olivier Thomann CLA 2001-11-29 12:57:45 EST
Why is it possible to select a position inside a line break?
Of course there is no protection against this. The line breaks are written in an 
atomic manner. This explains the offset by one after it.
I will try to find a solution for that, but I think such a position doesn't make 
sense to me.
Did you find another bug except this position inside a line break?
Comment 17 Olivier Thomann CLA 2001-11-29 14:07:14 EST
I have a fix for it and I added tests in the formatter tests suite. I simply 
didn't handle this case, because I thought it was not possible to put a marker 
inside a line break. Now it is handled and let me know if you have other 
problems with the mapping positions.
Release in HEAD.
Comment 18 Claude Knaus CLA 2001-11-30 05:40:12 EST
My position is before mapping is the *beginning* of the line-break, while
after the format, the mapped position happens to be *inside* the line-break. 
The correct behaviour would be for the position to stay at the beginning of
the line-break. My positionsToMap are valid, but not the mappedPositions.
Comment 19 Claude Knaus CLA 2001-11-30 06:01:34 EST
ignore my last comment, I didn't see your second comment. I'll check the jar 
and tell you if it works.
Comment 20 Olivier Thomann CLA 2001-11-30 09:49:50 EST
According to your test case, it should work. I map to a position that makes 
sense. The '#' is at the same place using your test class. Let me know if it 
doesn't work for you.
Comment 21 Claude Knaus CLA 2001-11-30 12:14:21 EST
I verified that this bug is fixed. Go ahead and release it.
However, I have seen other positions which didn't map correctly either... I 
think I'm going to write a small position map test for the code formatter...
Comment 22 Olivier Thomann CLA 2001-11-30 13:11:33 EST
Sure, I'd like to know when the positions are not mapped properly.