### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: buildnotes_jdt-core.html =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/buildnotes_jdt-core.html,v retrieving revision 1.7344 diff -u -r1.7344 buildnotes_jdt-core.html --- buildnotes_jdt-core.html 2 Mar 2010 15:58:29 -0000 1.7344 +++ buildnotes_jdt-core.html 2 Mar 2010 18:14:17 -0000 @@ -48,9 +48,168 @@
Project org.eclipse.jdt.core v_A38 (cvs).

What's new in this drop

+

Problem Reports Fixed

-304316 +27079 +Tags for disabling/enabling code formatter (feature) +
304316 NPE when javadoc URL is invalid Index: formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java,v retrieving revision 1.106 diff -u -r1.106 DefaultCodeFormatterConstants.java --- formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java 1 Mar 2010 17:09:32 -0000 1.106 +++ formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java 2 Mar 2010 18:14:19 -0000 @@ -856,6 +856,143 @@ public static final String FORMATTER_CONTINUATION_INDENTATION_FOR_ARRAY_INITIALIZER = JavaCore.PLUGIN_ID + ".formatter.continuation_indentation_for_array_initializer"; //$NON-NLS-1$ /** *
+	 * FORMATTER / Option to define the tag to put in a comment to disable the formatting.
+	 * See the {@link #FORMATTER_ENABLING_TAG} option to re-enable it.
+	 *     - option id:         "org.eclipse.jdt.core.formatter.disabling_tag"
+	 *     - default:           null
+	 * 
+ * + *

+ * Note that: + *

    + *
  1. As soon as the formatter encounters the defined disabling tag, it stops to + * format the code from the beginning of the comment including this tag. If it + * was already disabled, the tag has no special effect. + *

    + * For example, the second defined enabling tag "disable-formatter" + * in the following snippet is not necessary as the formatter was already disabled + * since the first one: + *

    +	 * class X {
    +	 * // disable-formatter
    +	 * void foo1() {}
    +	 * // disable-formatter
    +	 * void foo2() {}
    +	 * void bar1() {}
    +	 * void bar2() {}
    +	 * }
    +	 * 
    + *
  2. + *
  3. If no enabling tag is found by the formatter after the disabling tag, then + * the end of the snippet won't be formatted.
    + * For example, when a disabling tag is put at the beginning of the code, then + * the entire content of a compilation unit is not formatted: + *
    +	 * // disable-formatter
    +	 * class X {
    +	 * void foo1() {}
    +	 * void foo2() {}
    +	 * void bar1() {}
    +	 * void bar2() {}
    +	 * }
    +	 * 
    + *
  4. + *
  5. If a mix of disabling and enabling tags is done in the same comment, then + * the formatter will only take into account the last encountered tag in the + * comment. + *

    For example, in the following snippet, the formatter will be disabled after + * the comment:

    + *
    +	 * class X {
    +	 * /*
    +	 *  * This is a comment with a mix of disabling and enabling tags:
    +	 *  *  - disable-formatter
    +	 *  *  - enable-formatter
    +	 *  *  - disable-formatter
    +	 *  * The formatter will stop to format from the beginning of this comment...
    +	 *  */
    +	 * void foo() {}
    +	 * void bar() {}
    +	 * }
    +	 * 
    + *
  6. + *
  7. The tag cannot include newline characters but it can have white spaces.
    + * E.g. "format: off" is a valid disabling tag
  8. + *
+ *

+ * @since 3.6 + */ + public static final String FORMATTER_DISABLING_TAG = JavaCore.PLUGIN_ID + ".formatter.disabling_tag"; //$NON-NLS-1$ + /** + *
+	 * FORMATTER / Option to define the tag to put in a comment to re-enable the
+	 * formatting after it has been disabled (see {@link #FORMATTER_DISABLING_TAG})
+	 *     - option id:         "org.eclipse.jdt.core.formatter.enabling_tag"
+	 *     - default:           null
+	 * 
+ * + *

+ * Note that: + *

    + *
  1. As soon as the formatter encounters the defined enabling tag, it re-starts + * to format the code just after the comment including this tag. If it was already + * active, i.e. already re-enabled or never disabled, the tag has no special effect. + *

    + * For example, the defined enabling tag "enable-formatter" + * in the following snippet is not necessary as the formatter has never been + * disabled: + *

    +	 * class X {
    +	 * void foo1() {}
    +	 * void foo2() {}
    +	 * // enable-formatter
    +	 * void bar1() {}
    +	 * void bar2() {}
    +	 * }
    +	 * 
    + * Or, in the following other snippet, the second enabling tag is not necessary as + * the formatting will have been re-enabled by the first one: + *
    +	 * class X {
    +	 * // disable-formatter
    +	 * void foo1() {}
    +	 * void foo2() {}
    +	 * // enable-formatter
    +	 * void bar1() {}
    +	 * // enable-formatter
    +	 * void bar2() {}
    +	 * }
    +	 * 
    + *
  2. + *
  3. If a mix of disabling and enabling tags is done in the same comment, then + * the formatter will only take into account the last encountered tag in the + * comment. + *

    For example, in the following snippet, the formatter will be re-enabled after + * the comment:

    + *
    +	 * // disable-formatter
    +	 * class X {
    +	 * /*
    +	 *  * This is a comment with a mix of disabling and enabling tags:
    +	 *  *  - enable-formatter
    +	 *  *  - disable-formatter
    +	 *  *  - enable-formatter
    +	 *  * The formatter will restart to format after this comment...
    +	 *  */
    +	 * void foo() {}
    +	 * void bar() {}
    +	 * }
    +	 * 
    + *
  4. The tag cannot include newline characters but it can have white spaces.
    + * E.g. "format: on" is a valid enabling tag
  5. + * + *
+ *

+ * @since 3.6 + */ + public static final String FORMATTER_ENABLING_TAG = JavaCore.PLUGIN_ID + ".formatter.enabling_tag"; //$NON-NLS-1$ + /** + *
 	 * FORMATTER / Option to indent body declarations compare to its enclosing annotation declaration header
 	 *     - option id:         "org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header"
 	 *     - possible values:   { TRUE, FALSE }
Index: formatter/org/eclipse/jdt/internal/formatter/CodeFormatterVisitor.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CodeFormatterVisitor.java,v
retrieving revision 1.229
diff -u -r1.229 CodeFormatterVisitor.java
--- formatter/org/eclipse/jdt/internal/formatter/CodeFormatterVisitor.java	23 Feb 2010 15:04:52 -0000	1.229
+++ formatter/org/eclipse/jdt/internal/formatter/CodeFormatterVisitor.java	2 Mar 2010 18:14:21 -0000
@@ -756,7 +756,7 @@
 		final char[] compilationUnitSource = string.toCharArray();
 
 		this.localScanner.setSource(compilationUnitSource);
-		this.scribe.initializeScanner(compilationUnitSource);
+		this.scribe.resetScanner(compilationUnitSource);
 
 		if (nodes == null) {
 			return null;
@@ -793,7 +793,7 @@
 		final char[] compilationUnitSource = string.toCharArray();
 
 		this.localScanner.setSource(compilationUnitSource);
-		this.scribe.initializeScanner(compilationUnitSource);
+		this.scribe.resetScanner(compilationUnitSource);
 
 		this.lastLocalDeclarationSourceStart = -1;
 		try {
@@ -822,7 +822,7 @@
 		final char[] compilationUnitSource = string.toCharArray();
 
 		this.localScanner.setSource(compilationUnitSource);
-		this.scribe.initializeScanner(compilationUnitSource);
+		this.scribe.resetScanner(compilationUnitSource);
 
 		if (constructorDeclaration == null) {
 			return null;
@@ -866,7 +866,7 @@
 		final char[] compilationUnitSource = string.toCharArray();
 
 		this.localScanner.setSource(compilationUnitSource);
-		this.scribe.initializeScanner(compilationUnitSource);
+		this.scribe.resetScanner(compilationUnitSource);
 
 		if (expression == null) {
 			return null;
Index: formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java,v
retrieving revision 1.102
diff -u -r1.102 DefaultCodeFormatterOptions.java
--- formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java	1 Mar 2010 17:09:36 -0000	1.102
+++ formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java	2 Mar 2010 18:14:24 -0000
@@ -16,6 +16,7 @@
 
 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
 import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.internal.compiler.util.Util;
 import org.eclipse.jdt.internal.formatter.align.Alignment;
 
 /**
@@ -116,6 +117,9 @@
 	public boolean comment_insert_new_line_for_parameter;
 	public int comment_line_length;
 
+	public char[] disabling_tag;
+	public char[] enabling_tag;
+
 	public boolean indent_statements_compare_to_block;
 	public boolean indent_statements_compare_to_body;
 	public boolean indent_body_declarations_compare_to_annotation_declaration_header;
@@ -612,6 +616,8 @@
 		options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, Integer.toString(this.tab_size));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_USE_TABS_ONLY_FOR_LEADING_INDENTATIONS, this.use_tabs_only_for_leading_indentations ?  DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_WRAP_BEFORE_BINARY_OPERATOR, this.wrap_before_binary_operator ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
+		options.put(DefaultCodeFormatterConstants.FORMATTER_DISABLING_TAG, this.disabling_tag == null ? Util.EMPTY_STRING : new String(this.disabling_tag));
+		options.put(DefaultCodeFormatterConstants.FORMATTER_ENABLING_TAG, this.enabling_tag == null ? Util.EMPTY_STRING : new String(this.enabling_tag));
 		return options;
 	}
 
@@ -1949,6 +1955,28 @@
 		if (wrapBeforeBinaryOperatorOption != null) {
 			this.wrap_before_binary_operator = DefaultCodeFormatterConstants.TRUE.equals(wrapBeforeBinaryOperatorOption);
 		}
+		final Object disableTagOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_DISABLING_TAG);
+		if (disableTagOption != null) {
+			if (disableTagOption instanceof String) {
+				String stringValue = (String) disableTagOption;
+				if (stringValue.trim().length() == 0) {
+					this.disabling_tag = null;
+				} else {
+					this.disabling_tag = stringValue.toCharArray();
+				}
+			}
+		}
+		final Object enableTagOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ENABLING_TAG);
+		if (enableTagOption != null) {
+			if (enableTagOption instanceof String) {
+				String stringValue = (String) enableTagOption;
+				if (stringValue.trim().length() == 0) {
+					this.enabling_tag = null;
+				} else {
+					this.enabling_tag = stringValue.toCharArray();
+				}
+			}
+		}
 	}
 
 	/**
Index: formatter/org/eclipse/jdt/internal/formatter/FormatJavadocBlock.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/FormatJavadocBlock.java,v
retrieving revision 1.12
diff -u -r1.12 FormatJavadocBlock.java
--- formatter/org/eclipse/jdt/internal/formatter/FormatJavadocBlock.java	14 Feb 2010 15:57:25 -0000	1.12
+++ formatter/org/eclipse/jdt/internal/formatter/FormatJavadocBlock.java	2 Mar 2010 18:14:24 -0000
@@ -358,7 +358,11 @@
 	boolean inlined = (this.flags & INLINED) != 0;
 	if (inlined) buffer.append("	{"); //$NON-NLS-1$
 	buffer.append('@');
-	buffer.append(TAG_NAMES[this.tagValue]);
+	if (this.tagValue == TAG_OTHERS_VALUE) {
+		buffer.append("others_tag"); //$NON-NLS-1$
+	} else {
+		buffer.append(TAG_NAMES[this.tagValue]);
+	}
 	super.toString(buffer);
 	if (this.reference == null) {
 		buffer.append('\n');
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.195
diff -u -r1.195 Scribe.java
--- formatter/org/eclipse/jdt/internal/formatter/Scribe.java	1 Mar 2010 17:09:35 -0000	1.195
+++ formatter/org/eclipse/jdt/internal/formatter/Scribe.java	2 Mar 2010 18:14:25 -0000
@@ -78,10 +78,14 @@
 
 	private int[] lineEnds;
 	private int maxLines;
-	private String lineSeparator;
 	public Alignment memberAlignment;
 	public boolean needSpace = false;
 
+	// Line separator infos
+	final private String lineSeparator;
+	final private char firstLS;
+	final private int lsLength;
+
 	public int nlsTagCounter;
 	public int pageWidth;
 	public boolean pendingSpace = false;
@@ -100,6 +104,9 @@
 	private final boolean indentEmptyLines;
 	int blank_lines_between_import_groups = -1;
 
+	/** disabling */
+	boolean editsEnabled = true;
+
 	/* Comments formatting */
 	private static final int INCLUDE_BLOCK_COMMENTS = CodeFormatter.F_INCLUDE_COMMENTS | CodeFormatter.K_MULTI_LINE_COMMENT;
 	private static final int INCLUDE_JAVA_DOC = CodeFormatter.F_INCLUDE_COMMENTS | CodeFormatter.K_JAVA_DOC;
@@ -114,6 +121,7 @@
 	private int formatComments = 0;
 	private int headerEndPosition = -1;
 	String commentIndentation; // indentation requested in comments (usually in javadoc root tags description)
+
 	// Class to store previous line comment information
 	static class LineComment {
 		boolean contiguous = false;
@@ -123,12 +131,15 @@
 	}
 	final LineComment lastLineComment = new LineComment();
 
-
 	// New way to format javadoc
 	private FormatterCommentParser formatterCommentParser; // specialized parser to format comments
 
+	// Disabling and enabling tags
+	OptimizedReplaceEdit previousDisabledEdit;
+	private char[] disablingTag, enablingTag;
+
 	Scribe(CodeFormatterVisitor formatter, long sourceLevel, IRegion[] regions, CodeSnippetParsingUtil codeSnippetParsingUtil, boolean includeComments) {
-		this.scanner = new Scanner(true, true, false/*nls*/, sourceLevel/*sourceLevel*/, null/*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
+		initializeScanner(sourceLevel, formatter.preferences);
 		this.formatter = formatter;
 		this.pageWidth = formatter.preferences.page_width;
 		this.tabLength = formatter.preferences.tab_size;
@@ -143,6 +154,8 @@
 			this.indentationSize = this.tabLength;
 		}
 		this.lineSeparator = formatter.preferences.line_separator;
+		this.firstLS = this.lineSeparator.charAt(0);
+		this.lsLength = this.lineSeparator.length();
 		this.indentationLevel = formatter.preferences.initial_indentation_level * this.indentationSize;
 		this.regions= regions;
 		if (codeSnippetParsingUtil != null) {
@@ -447,6 +460,18 @@
 	}
 
 	private final void addOptimizedReplaceEdit(int offset, int length, String replacement) {
+		if (!this.editsEnabled) {
+			if (this.previousDisabledEdit != null && this.previousDisabledEdit.offset == offset) {
+				replacement = this.previousDisabledEdit.replacement;
+			}
+			this.previousDisabledEdit = null;
+			if (replacement.indexOf(this.lineSeparator) >= 0) {
+				if (length == 0 || printNewLinesCharacters(offset, length)) {
+					this.previousDisabledEdit = new OptimizedReplaceEdit(offset, length, replacement);
+				}
+			}
+			return;
+		}
 		if (this.editsIndex > 0) {
 			// try to merge last two edits
 			final OptimizedReplaceEdit previous = this.edits[this.editsIndex-1];
@@ -1269,18 +1294,16 @@
 		this.numberOfIndentations++;
 	}
 
-	/**
-	 * @param compilationUnitSource
-	 */
-	public void initializeScanner(char[] compilationUnitSource) {
-		this.scanner.setSource(compilationUnitSource);
-		this.scannerEndPosition = compilationUnitSource.length;
-		this.scanner.resetTo(0, this.scannerEndPosition - 1);
-		this.edits = new OptimizedReplaceEdit[INITIAL_SIZE];
-		this.maxLines = this.lineEnds == null ? -1 : this.lineEnds.length - 1;
-		this.scanner.lineEnds = this.lineEnds;
-		this.scanner.linePtr = this.maxLines;
-		initFormatterCommentParser();
+	private void initializeScanner(long sourceLevel, DefaultCodeFormatterOptions preferences) {
+		this.disablingTag = preferences.disabling_tag;
+		this.enablingTag = preferences.enabling_tag;
+		char[][] taskTags;
+		if (this.disablingTag == null && this.enablingTag == null) {
+			taskTags = null;
+		} else {
+			taskTags = new char[][] { this.disablingTag, this.enablingTag };
+		}
+		this.scanner = new Scanner(true, true, false/*nls*/, sourceLevel/*sourceLevel*/, taskTags, null/*taskPriorities*/, true/*taskCaseSensitive*/);
 	}
 
 	private void initFormatterCommentParser() {
@@ -2153,7 +2176,9 @@
 			boolean hasLineComment = false;
 			boolean hasWhitespaces = false;
 			int lines = 0;
+			int previousFoundTaskCount = this.scanner.foundTaskCount;
 			while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) {
+				int foundTaskCount = this.scanner.foundTaskCount;
 				switch(this.currentToken) {
 					case TerminalTokens.TokenNameWHITESPACE :
 						char[] whiteSpaces = this.scanner.getCurrentTokenSource();
@@ -2279,6 +2304,16 @@
 						currentTokenStartPosition = this.scanner.currentPosition;
 						break;
 					case TerminalTokens.TokenNameCOMMENT_LINE :
+						if (this.editsEnabled && foundTaskCount > previousFoundTaskCount) {
+							setEditsEnabled(foundTaskCount, previousFoundTaskCount);
+							if (!this.editsEnabled && this.editsIndex > 1) {
+								OptimizedReplaceEdit currentEdit = this.edits[this.editsIndex-1];
+								if (this.scanner.startPosition == currentEdit.offset+currentEdit.length) {
+									printNewLinesBeforeDisablingComment();
+								}
+							}
+							previousFoundTaskCount = foundTaskCount;
+						}
 						if (rejectLineComment) break;
 						if (lines >= 1) {
 							if (lines > 1) {
@@ -2294,8 +2329,21 @@
 						currentTokenStartPosition = this.scanner.currentPosition;
 						hasLineComment = true;
 						lines = 0;
+						if (!this.editsEnabled && foundTaskCount > previousFoundTaskCount) {
+							setEditsEnabled(foundTaskCount, previousFoundTaskCount);
+						}
 						break;
 					case TerminalTokens.TokenNameCOMMENT_BLOCK :
+						if (this.editsEnabled && foundTaskCount > previousFoundTaskCount) {
+							setEditsEnabled(foundTaskCount, previousFoundTaskCount);
+							if (!this.editsEnabled && this.editsIndex > 1) {
+								OptimizedReplaceEdit currentEdit = this.edits[this.editsIndex-1];
+								if (this.scanner.startPosition == currentEdit.offset+currentEdit.length) {
+									printNewLinesBeforeDisablingComment();
+								}
+							}
+							previousFoundTaskCount = foundTaskCount;
+						}
 						if (trailing > NO_TRAILING_COMMENT && lines >= 1) {
 							// a block comment on next line means that there's no trailing comment
 							this.scanner.resetTo(this.scanner.getCurrentTokenStartPosition(), this.scannerEndPosition - 1);
@@ -2318,8 +2366,15 @@
 						hasLineComment = false;
 						hasComment = true;
 						lines = 0;
+						if (!this.editsEnabled && foundTaskCount > previousFoundTaskCount) {
+							setEditsEnabled(foundTaskCount, previousFoundTaskCount);
+						}
 						break;
 					case TerminalTokens.TokenNameCOMMENT_JAVADOC :
+						if (this.editsEnabled && foundTaskCount > previousFoundTaskCount) {
+							setEditsEnabled(foundTaskCount, previousFoundTaskCount);
+							previousFoundTaskCount = foundTaskCount;
+						}
 						if (trailing > NO_TRAILING_COMMENT) {
 							// a javadoc comment should not be considered as a trailing comment
 							this.scanner.resetTo(this.scanner.getCurrentTokenStartPosition(), this.scannerEndPosition - 1);
@@ -2342,6 +2397,9 @@
 						} else {
 							printBlockComment(true);
 						}
+						if (!this.editsEnabled && foundTaskCount > previousFoundTaskCount) {
+							setEditsEnabled(foundTaskCount, previousFoundTaskCount);
+						}
 						printNewLine();
 						currentTokenStartPosition = this.scanner.currentPosition;
 						hasLineComment = false;
@@ -2354,6 +2412,7 @@
 						this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
 						return;
 				}
+				previousFoundTaskCount = foundTaskCount;
 			}
 		} catch (InvalidInputException e) {
 			throw new AbortFormatting(e);
@@ -2363,7 +2422,7 @@
 	void printComment(int kind, String source, int start, int end, int level) {
 
 		// Set scanner
-		initializeScanner(source.toCharArray());
+		resetScanner(source.toCharArray());
 		this.scanner.resetTo(start, end);
 		// Put back 3.4RC2 code => comment following line  as it has an impact on Linux tests
 		// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=234336
@@ -2677,9 +2736,10 @@
 			}
 		}
 
-		// Delete leading whitespaces if any
-		if (previousToken != -1 && lastTokenEndPosition != commentStart && spaceEndPosition > lastTokenEndPosition) {
-			addDeleteEdit(lastTokenEndPosition, spaceEndPosition-1);
+		// Replace the line separator at the end of the comment if any...
+		int startReplace = previousToken == SKIP_FIRST_WHITESPACE_TOKEN ? spaceStartPosition : lastTokenEndPosition;
+		if (this.column == 1 && commentEnd >= startReplace) {
+			addReplaceEdit(startReplace, commentEnd, this.formatter.preferences.line_separator);
 		}
 	}
 
@@ -4319,6 +4379,135 @@
 		this.lastLineComment.contiguous = false;
 	}
 
+	/*
+	 * Print the indentation of a disabling comment
+	 */
+	private void printNewLinesBeforeDisablingComment() {
+
+		// Get the beginning of comment line
+		int linePtr = Arrays.binarySearch(this.lineEnds, this.scanner.startPosition);
+		if (linePtr < 0) {
+			linePtr = -linePtr - 1;
+		}
+		int indentation = 0;
+		int beginningOfLine = getLineEnd(linePtr)+1;
+		if (beginningOfLine == -1) {
+			beginningOfLine = 0;
+		}
+		
+		// If the comment is in the middle of the line, then there's nothing to do
+		OptimizedReplaceEdit currentEdit = this.edits[this.editsIndex-1];
+		int offset = currentEdit.offset;
+		if (offset >= beginningOfLine) return;
+
+		// Compute the comment indentation
+		int scannerStartPosition = this.scanner.startPosition;
+		int scannerEofPosition = this.scanner.eofPosition;
+		int scannerCurrentPosition = this.scanner.currentPosition;
+		char scannerCurrentChar = this.scanner.currentCharacter;
+		int length = currentEdit.length;
+		this.scanner.resetTo(beginningOfLine, offset+length-1);
+		try {
+			while (!this.scanner.atEnd()) {
+				char ch = (char) this.scanner.getNextChar();
+				switch (ch) {
+					case '\t' :
+						if (this.tabLength != 0) {
+							int reminder = indentation % this.tabLength;
+							if (reminder == 0) {
+								indentation += this.tabLength;
+							} else {
+								indentation = ((indentation / this.tabLength) + 1) * this.tabLength;
+							}
+						}
+						break;
+					case ' ':
+						indentation++;
+						break;
+					default:
+						// Should not happen as the offset of the edit is before the beginning of line
+						return;
+				}
+			}
+		
+			// Split the existing edit to keep the change before the beginning of the last line
+			// but change the indentation after. Note that at this stage, the add*Edit methods
+			// cannot be longer used as the edits are disabled
+			StringBuffer indentationBuffer = new StringBuffer();
+			int currentIndentation = getCurrentIndentation(this.scanner.currentPosition);
+			if (currentIndentation > 0 && this.indentationLevel > 0) {
+				int col = this.column;
+				printIndentationIfNecessary(indentationBuffer);
+				this.column = col;
+			}
+			String replacement = currentEdit.replacement;
+			if (replacement.length() == 0) {
+				// previous edit was a delete, as we're sure to have a new line before
+				// the comment, then the edit needs to be either replaced entirely with
+				// the expected indentation
+				this.edits[this.editsIndex-1] = new OptimizedReplaceEdit(beginningOfLine, offset+length-beginningOfLine, indentationBuffer.toString());
+			} else {
+				int idx = replacement.lastIndexOf(this.lineSeparator);
+				if (idx >= 0) {
+					// replace current edit if it contains a line separator
+					int start = idx + this.lsLength;
+					StringBuffer buffer = new StringBuffer(replacement.substring(0, start));
+					buffer.append(indentationBuffer);
+					this.edits[this.editsIndex-1] = new OptimizedReplaceEdit(offset, length, buffer.toString());
+				}
+			}
+		}
+		finally {
+			this.scanner.startPosition = scannerStartPosition;
+			this.scanner.eofPosition = scannerEofPosition;
+			this.scanner.currentPosition = scannerCurrentPosition;
+			this.scanner.currentCharacter = scannerCurrentChar;
+		}
+	}
+
+	/*
+	 * Print new lines characters when the edits are disabled. In this case, only
+	 * the line separator is replaced if necessary, the other white spaces are untouched.
+	 */
+	private boolean printNewLinesCharacters(int offset, int length) {
+		boolean foundNewLine = false;
+		int scannerStartPosition = this.scanner.startPosition;
+		int scannerEofPosition = this.scanner.eofPosition;
+		int scannerCurrentPosition = this.scanner.currentPosition;
+		char scannerCurrentChar = this.scanner.currentCharacter;
+		this.scanner.resetTo(offset, offset+length-1);
+		try {
+			while (!this.scanner.atEnd()) {
+				int start = this.scanner.currentPosition;
+				char ch = (char) this.scanner.getNextChar();
+				boolean needReplace = ch != this.firstLS;
+				switch (ch) {
+					case '\r':
+						if (this.scanner.atEnd()) break;
+						ch = (char) this.scanner.getNextChar();
+						if (ch != '\n') break;
+						needReplace = needReplace || this.lsLength != 2;
+						//$FALL-THROUGH$
+					case '\n':
+						if (needReplace) {
+							if (this.editsIndex == 0 || this.edits[this.editsIndex-1].offset != start) {
+								this.edits[this.editsIndex++] = new OptimizedReplaceEdit(start, this.scanner.currentPosition-start, this.lineSeparator);
+							}
+						}
+						foundNewLine = true;
+						break;
+				}
+			}
+		}
+		finally {
+			this.scanner.startPosition = scannerStartPosition;
+			this.scanner.eofPosition = scannerEofPosition;
+			this.scanner.currentPosition = scannerCurrentPosition;
+			this.scanner.currentCharacter = scannerCurrentChar;
+		}
+		return foundNewLine;		
+	}
+
 	public void printNextToken(int expectedTokenType){
 		printNextToken(expectedTokenType, false);
 	}
@@ -4526,10 +4715,42 @@
 		this.formatter.lastLocalDeclarationSourceStart = location.lastLocalDeclarationSourceStart;
 	}
 
+	/**
+	 * @param compilationUnitSource
+	 */
+	public void resetScanner(char[] compilationUnitSource) {
+		this.scanner.setSource(compilationUnitSource);
+		this.scannerEndPosition = compilationUnitSource.length;
+		this.scanner.resetTo(0, this.scannerEndPosition - 1);
+		this.edits = new OptimizedReplaceEdit[INITIAL_SIZE];
+		this.maxLines = this.lineEnds == null ? -1 : this.lineEnds.length - 1;
+		this.scanner.lineEnds = this.lineEnds;
+		this.scanner.linePtr = this.maxLines;
+		initFormatterCommentParser();
+	}
+
 	private void resize() {
 		System.arraycopy(this.edits, 0, (this.edits = new OptimizedReplaceEdit[this.editsIndex * 2]), 0, this.editsIndex);
 	}
 
+	/*
+	 * Look for the tags identified by the scanner to see whether some of them
+	 * may change the status of the edition for the formatter.
+	 * Do not return as soon as a match is found, as there may have several
+	 * disabling/enabling tags in a comment, hence the last one will be the one really
+	 * changing the formatter behavior...
+	 */
+	private void setEditsEnabled(int count, int previous) {
+		for (int i=previous; i