From 56cae6fd61112f3f8c3be6c9e7c134955b25a286 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Sun, 27 Mar 2011 04:48:05 +0000 Subject: [PATCH] Formatting of stream output expressions. --- .../dom/ast/cpp/ICPPASTBinaryExpression.java | 8 ++ .../DefaultCodeFormatterConstants.java | 17 ++- .../internal/formatter/CCodeFormatter.java | 27 ++-- .../formatter/CodeFormatterVisitor.java | 119 ++++++++++++++-- .../DefaultCodeFormatterOptions.java | 13 ++ .../internal/formatter/align/Alignment.java | 3 +- .../cdt/ui/tests/text/CodeFormatterTest.java | 83 ++++++++---- .../formatter/FormatterMessages.java | 6 +- .../formatter/FormatterMessages.properties | 6 +- .../formatter/LineWrappingTabPage.java | 128 +++++++++++++----- .../formatter/TranslationUnitPreview.java | 10 +- 11 files changed, 317 insertions(+), 103 deletions(-) diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTBinaryExpression.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTBinaryExpression.java index 12f58a73c92..b1bf7a0e199 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTBinaryExpression.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTBinaryExpression.java @@ -8,6 +8,7 @@ * Contributors: * John Camelon (IBM) - Initial API and implementation * Mike Kucera (IBM) + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.core.dom.ast.cpp; @@ -48,4 +49,11 @@ public interface ICPPASTBinaryExpression extends IASTBinaryExpression, IASTImpli * @since 5.3 */ public ICPPASTBinaryExpression copy(CopyStyle style); + + /** + * Returns the function binding for the overloaded operator, or null if + * the operator is not overloaded. + * @since 5.3 + */ + public ICPPFunction getOverload(); } diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java index 573f3c97ea5..a9aa0376a25 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java @@ -201,7 +201,18 @@ public class DefaultCodeFormatterConstants { * @see #createAlignmentValue(boolean, int, int) * @since 5.3 */ - public static final String FORMATTER_ALIGNMENT_FOR_MEMBER_ACCESS = CCorePlugin.PLUGIN_ID + ".formatter.alignment_for_member_access"; //$NON-NLS-1$ + public static final String FORMATTER_ALIGNMENT_FOR_MEMBER_ACCESS = CCorePlugin.PLUGIN_ID + ".formatter.alignment_for_member_access"; //$NON-NLS-1$ + /** + *
+	 * FORMATTER / Option for alignment of stream output expression consisting of a chain of
+	 * overloaded << operators.
+	 *     - option id:         "org.eclipse.cdt.core.formatter.alignment_for_overloaded_left_shift_chainn"
+	 *     - possible values:   values returned by createAlignmentValue(boolean, int, int) call
+	 *     - default:           createAlignmentValue(false, WRAP_COMPACT, INDENT_DEFAULT)
+	 * 
+ * @see #createAlignmentValue(boolean, int, int) + */ + public static final String FORMATTER_ALIGNMENT_FOR_OVERLOADED_LEFT_SHIFT_CHAIN = CCorePlugin.PLUGIN_ID + ".formatter.alignment_for_overloaded_left_shift_chain"; //$NON-NLS-1$; /** *
 	 * FORMATTER / Option for alignment of parameters in method declaration
@@ -211,7 +222,7 @@ public class DefaultCodeFormatterConstants {
 	 * 
* @see #createAlignmentValue(boolean, int, int) */ - public static final String FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION = CCorePlugin.PLUGIN_ID + ".formatter.alignment_for_parameters_in_method_declaration"; //$NON-NLS-1$ + public static final String FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION = CCorePlugin.PLUGIN_ID + ".formatter.alignment_for_parameters_in_method_declaration"; //$NON-NLS-1$ // /** // *
 //	 * FORMATTER / Option for alignment of selector in method invocation
@@ -2268,7 +2279,7 @@ public class DefaultCodeFormatterConstants {
 	 * Private constants.
 	 */
 	private static final IllegalArgumentException WRONG_ARGUMENT = new IllegalArgumentException();
-	
+
 	/**
 	 * Create a new alignment value according to the given values. This must be used to set up
 	 * the alignment options.
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CCodeFormatter.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CCodeFormatter.java
index 615d46a590b..a06323d54d5 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CCodeFormatter.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CCodeFormatter.java
@@ -123,19 +123,19 @@ public class CCodeFormatter extends CodeFormatter {
 	@Override
 	public TextEdit format(int kind, String source, int offset, int length, int indentationLevel, String lineSeparator) {
 		TextEdit edit= null;
-		ITranslationUnit tu= (ITranslationUnit)options.get(DefaultCodeFormatterConstants.FORMATTER_TRANSLATION_UNIT);
+		ITranslationUnit tu= (ITranslationUnit) options.get(DefaultCodeFormatterConstants.FORMATTER_TRANSLATION_UNIT);
 		if (tu == null) {
-			IFile file= (IFile)options.get(DefaultCodeFormatterConstants.FORMATTER_CURRENT_FILE);
+			IFile file= (IFile) options.get(DefaultCodeFormatterConstants.FORMATTER_CURRENT_FILE);
 			if (file != null) {
-				tu= (ITranslationUnit)CoreModel.getDefault().create(file);
+				tu= (ITranslationUnit) CoreModel.getDefault().create(file);
 			}
 		}
 		if (lineSeparator != null) {
-			this.preferences.line_separator = lineSeparator;
+			preferences.line_separator = lineSeparator;
 		} else {
-			this.preferences.line_separator = System.getProperty("line.separator"); //$NON-NLS-1$
+			preferences.line_separator = System.getProperty("line.separator"); //$NON-NLS-1$
 		}
-		this.preferences.initial_indentation_level = indentationLevel;
+		preferences.initial_indentation_level = indentationLevel;
 
 		if (tu != null) {
 			IIndex index;
@@ -154,7 +154,7 @@ public class CCodeFormatter extends CodeFormatter {
 				} catch (CoreException exc) {
 					throw new AbortFormatting(exc);
 				}
-				CodeFormatterVisitor codeFormatter = new CodeFormatterVisitor(this.preferences, offset, length);
+				CodeFormatterVisitor codeFormatter = new CodeFormatterVisitor(preferences, offset, length);
 				edit= codeFormatter.format(source, ast);
 				IStatus status= codeFormatter.getStatus();
 				if (!status.isOK()) {
@@ -165,22 +165,21 @@ public class CCodeFormatter extends CodeFormatter {
 			}
 		} else {
 			IncludeFileContentProvider contentProvider = IncludeFileContentProvider.getSavedFilesProvider();
-			
 			IScannerInfo scanInfo = new ScannerInfo();
-			
 			FileContent content = FileContent.create("", source.toCharArray()); //$NON-NLS-1$
 			
-			ILanguage language= (ILanguage)options.get(DefaultCodeFormatterConstants.FORMATTER_LANGUAGE);
+			ILanguage language= (ILanguage) options.get(DefaultCodeFormatterConstants.FORMATTER_LANGUAGE);
 			if (language == null) {
 				language= GPPLanguage.getDefault();
 			}
 			IASTTranslationUnit ast;
 			try {
-				ast= language.getASTTranslationUnit(content, scanInfo, contentProvider, null, 0, ParserUtil.getParserLogService());
-				CodeFormatterVisitor codeFormatter = new CodeFormatterVisitor(this.preferences, offset, length);
+				ast= language.getASTTranslationUnit(content, scanInfo, contentProvider, null, 0,
+						ParserUtil.getParserLogService());
+				CodeFormatterVisitor codeFormatter = new CodeFormatterVisitor(preferences, offset, length);
 				edit= codeFormatter.format(source, ast);
-			} catch (CoreException exc) {
-				throw new AbortFormatting(exc);
+			} catch (CoreException e) {
+				throw new AbortFormatting(e);
 			}
 		}
 		return edit;
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
index 034819153f1..59ecfc3d762 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
@@ -100,6 +100,7 @@ import org.eclipse.cdt.core.dom.ast.c.ICASTDesignatedInitializer;
 import org.eclipse.cdt.core.dom.ast.c.ICASTDesignator;
 import org.eclipse.cdt.core.dom.ast.c.ICASTTypeIdInitializerExpression;
 import org.eclipse.cdt.core.dom.ast.c.ICASTVisitor;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTBinaryExpression;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCastExpression;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
@@ -204,6 +205,10 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
 			this.spaceAfterToken = spaceAfterToken;
 		}
 
+		TrailingTokenFormatter(int tokenType, boolean spaceBeforeToken, boolean spaceAfterToken) {
+			this(tokenType, scribe.findToken(tokenType), spaceBeforeToken, spaceAfterToken);
+		}
+
 		public void run() {
 			int offset = scribe.scanner.getCurrentPosition();
 			if (tokenPosition < 0 || offset > tokenPosition)
@@ -225,18 +230,6 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
 		}
 	}
 
-	/*
-	 * Formats a trailing comma.
-	 * @see #formatList(List, ListOptions, boolean, boolean, Runnable)
-	 */
-	class TrailingSeparatorFormatter extends TrailingTokenFormatter {
-		TrailingSeparatorFormatter(int separatorToken, boolean spaceBeforeSeparator,
-				boolean spaceAfterSeparator) {
-			super(separatorToken, scribe.findToken(separatorToken), spaceBeforeSeparator,
-					spaceAfterSeparator);
-		}
-	}
-
 	/*
 	 * Formats a trailing semicolon.
 	 * @see #formatList(List, ListOptions, boolean, boolean, Runnable)
@@ -1222,7 +1215,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
 	private int visit(ICPPASTConstructorInitializer node) {
 		if (!startNode(node)) { return PROCESS_SKIP; }
 		try {
-			// format like a function call
+			// Format like a function call
 			formatFunctionCallArguments(node.getArguments());
 		} finally {
 			endOfNode(node);
@@ -2046,7 +2039,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
 								final IASTNode node= elements.get(i);
 								if (i < alignment.fragmentCount - 1) {
 									scribe.setTailFormatter(
-											new TrailingSeparatorFormatter(options.fSeparatorToken,
+											new TrailingTokenFormatter(options.fSeparatorToken,
 													options.fSpaceBeforeSeparator,
 													options.fSpaceAfterSeparator));
 								} else {
@@ -2249,6 +2242,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
 			expressions= Collections.emptyList();
 		}
 		final ListOptions options= new ListOptions(preferences.alignment_for_arguments_in_method_invocation);
+		options.fSeparatorToken = Token.tCOMMA;
 		options.fInsertNewLineBeforeListIfNecessary= true;
 		options.fSpaceBeforeOpeningParen= preferences.insert_space_before_opening_paren_in_method_invocation;
 		options.fSpaceAfterOpeningParen= preferences.insert_space_after_opening_paren_in_method_invocation;
@@ -2540,6 +2534,9 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
 		if (isAssignment(node)) {
 			return formatAssignment(node);
 		}
+		if (isOverloadedLeftShift(node)) {
+			return formatOverloadedLeftShiftChain(node);
+		}
 
 		Runnable tailFormatter = scribe.getTailFormatter();
 
@@ -2658,6 +2655,100 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
 		return false;
 	}
 
+	private int formatOverloadedLeftShiftChain(IASTBinaryExpression binaryExpression) {
+		List elements = new ArrayList();
+		IASTExpression node;
+		do {
+			elements.add(binaryExpression.getOperand2());
+			node = binaryExpression.getOperand1();
+			if (!(node instanceof IASTBinaryExpression)) {
+				break;
+			}
+			binaryExpression = (IASTBinaryExpression) node;
+		} while (isOverloadedLeftShift(binaryExpression));
+		Collections.reverse(elements);
+
+		node.accept(this);
+		scribe.printComment();
+		if (preferences.insert_space_before_binary_operator) {
+			scribe.space();
+		}
+
+		Runnable tailFormatter = scribe.getTailFormatter();
+		
+		Alignment wrapperAlignment = null;
+		if ((preferences.alignment_for_overloaded_left_shift_chain & Alignment.M_INDENT_ON_COLUMN) != 0) {
+			wrapperAlignment = scribe.createAlignment(
+					Alignment.LIST_WRAPPER,
+					Alignment.M_COMPACT_FIRST_BREAK_SPLIT,
+					Alignment.R_INNERMOST,
+					1,
+					scribe.scanner.getCurrentPosition(),
+					preferences.continuation_indentation,
+					false);
+			scribe.enterAlignment(wrapperAlignment);
+		}
+		boolean success = false;
+		do {
+			if (wrapperAlignment != null)
+				scribe.alignFragment(wrapperAlignment, 0);
+
+			try {
+				Alignment alignment = scribe.createAlignment(
+						Alignment.OVERLOADED_LEFT_SHIFT_CHAIN,
+						preferences.alignment_for_overloaded_left_shift_chain,
+						Alignment.R_OUTERMOST,
+						elements.size(),
+						scribe.scanner.getCurrentPosition(),
+						wrapperAlignment == null ? preferences.continuation_indentation : 0,
+						false);
+				scribe.enterAlignment(alignment);
+				boolean ok = false;
+				do {
+					try {
+						for (int i = 0; i < elements.size(); i++) {
+							scribe.alignFragment(alignment, i);
+							int token= peekNextToken();
+							if (token == Token.tSHIFTL) {
+								scribe.printNextToken(token, preferences.insert_space_before_binary_operator);
+								scribe.printTrailingComment();
+								if (preferences.insert_space_after_binary_operator) {
+									scribe.space();
+								}
+							}
+							node= elements.get(i);
+							if (i == alignment.fragmentCount - 1) {
+								scribe.setTailFormatter(tailFormatter);
+							}
+							node.accept(this);
+						}
+						scribe.runTailFormatter();
+						ok = true;
+					} catch (AlignmentException e) {
+						scribe.redoAlignment(e);
+					} catch (ASTProblemException e) {
+					}
+				} while (!ok);
+				scribe.exitAlignment(alignment, true);
+				success = true;
+			} catch (AlignmentException e) {
+				if (wrapperAlignment == null)
+					throw e;
+				scribe.redoAlignment(e);
+			}
+		} while (wrapperAlignment != null && !success);
+		if (wrapperAlignment != null)
+			scribe.exitAlignment(wrapperAlignment, true);
+
+    	return PROCESS_SKIP;
+	}
+
+	private boolean isOverloadedLeftShift(IASTBinaryExpression node) {
+		return node.getOperator() == IASTBinaryExpression.op_shiftLeft && 
+				node instanceof ICPPASTBinaryExpression &&
+				((ICPPASTBinaryExpression) node).getOverload() != null;
+	}
+
 	private int visit(IASTLiteralExpression node) {
 		if (node.getKind() == IASTLiteralExpression.lk_string_literal) {
 			// handle concatenation of string literals
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/DefaultCodeFormatterOptions.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/DefaultCodeFormatterOptions.java
index 7b54068706f..cff64a79311 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/DefaultCodeFormatterOptions.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/DefaultCodeFormatterOptions.java
@@ -65,6 +65,7 @@ public class DefaultCodeFormatterOptions {
 	public int alignment_for_expression_list;
 	public int alignment_for_expressions_in_initializer_list;
 	public int alignment_for_member_access;
+	public int alignment_for_overloaded_left_shift_chain;
 	public int alignment_for_parameters_in_method_declaration;
 	public int alignment_for_throws_clause_in_method_declaration;
 	public int alignment_for_constructor_initializer_list;
@@ -279,6 +280,7 @@ public class DefaultCodeFormatterOptions {
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_INITIALIZER_LIST, getAlignment(this.alignment_for_expressions_in_initializer_list));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSION_LIST, getAlignment(this.alignment_for_expression_list));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_MEMBER_ACCESS, getAlignment(this.alignment_for_member_access));
+		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_OVERLOADED_LEFT_SHIFT_CHAIN, getAlignment(this.alignment_for_overloaded_left_shift_chain));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION, getAlignment(this.alignment_for_parameters_in_method_declaration));
 //		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_SELECTOR_IN_METHOD_INVOCATION, getAlignment(this.alignment_for_selector_in_method_invocation));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_BASE_CLAUSE_IN_TYPE_DECLARATION, getAlignment(this.alignment_for_base_clause_in_type_declaration));
@@ -581,6 +583,16 @@ public class DefaultCodeFormatterOptions {
 				this.alignment_for_member_access =  Alignment.M_ONE_PER_LINE_SPLIT;
 			}
 		}
+		final Object alignmentForOverloadedLeftShiftChainOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_OVERLOADED_LEFT_SHIFT_CHAIN);
+		if (alignmentForOverloadedLeftShiftChainOption != null) {
+			try {
+				this.alignment_for_overloaded_left_shift_chain = Integer.parseInt((String) alignmentForOverloadedLeftShiftChainOption);
+			} catch (NumberFormatException e) {
+				this.alignment_for_overloaded_left_shift_chain = Alignment.M_COMPACT_SPLIT;
+			} catch (ClassCastException e) {
+				this.alignment_for_overloaded_left_shift_chain = Alignment.M_COMPACT_SPLIT;
+			}
+		}
 		final Object alignmentForParametersInMethodDeclarationOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
 		if (alignmentForParametersInMethodDeclarationOption != null) {
 			try {
@@ -1477,6 +1489,7 @@ public class DefaultCodeFormatterOptions {
 		this.alignment_for_enumerator_list = Alignment.M_ONE_PER_LINE_SPLIT;
 		this.alignment_for_expressions_in_initializer_list = Alignment.M_COMPACT_SPLIT;
 		this.alignment_for_member_access = Alignment.M_NO_ALIGNMENT;
+		this.alignment_for_overloaded_left_shift_chain = Alignment.M_COMPACT_SPLIT;
 		this.alignment_for_parameters_in_method_declaration = Alignment.M_COMPACT_SPLIT;
 //		this.alignment_for_selector_in_method_invocation = Alignment.M_COMPACT_SPLIT;
 		this.alignment_for_throws_clause_in_method_declaration = Alignment.M_COMPACT_SPLIT;
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/align/Alignment.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/align/Alignment.java
index c0a9218b470..48ded408adc 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/align/Alignment.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/align/Alignment.java
@@ -33,8 +33,9 @@ public class Alignment {
 	public static final String LIST_ELEMENTS_PREFIX = "listElements_"; //$NON-NLS-1$
 	public static final String LIST_WRAPPER = "listWrapper"; //$NON-NLS-1$
 	public static final String MACRO_ARGUMENTS = "macroArguments"; //$NON-NLS-1$
+	public static final String OVERLOADED_LEFT_SHIFT_CHAIN = "overloadedLeftShiftChain"; //$NON-NLS-1$
 	public static final String TRAILING_TEXT = "trailingText"; //$NON-NLS-1$
-	
+
 	/** The name of the alignment */
 	public String name;
 
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java
index d96b64338ba..ed1d2bf0a6a 100644
--- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java
@@ -1986,30 +1986,12 @@ public class CodeFormatterTest extends BaseUITestCase {
 		assertFormatterResult();
 	}
 
-	//class Stream {
-	//Stream& operator <<(const char*);
-	//Stream& operator <<(int);
-	//};
-	//
-	//Stream stream;
-	//
-	//void test() {
-	// // Breaking at << is preferred to breaking at +.
-	//stream << "text text text text text text text text text" << 1000000 + 2000000;
-	//}
+	//// Breaking at '<=' is preferred to breaking at '+'.
+	//bool x = 1000000 + 2000000 + 3000000 + 4000000 <= 5000000 + 6000000 + 7000000 + 8000000;
 
-	//class Stream {
-	//    Stream& operator <<(const char*);
-	//    Stream& operator <<(int);
-	//};
-	//
-	//Stream stream;
-	//
-	//void test() {
-	//    // Breaking at << is preferred to breaking at +.
-	//    stream << "text text text text text text text text text"
-	//            << 1000000 + 2000000;
-	//}
+	//// Breaking at '<=' is preferred to breaking at '+'.
+	//bool x = 1000000 + 2000000 + 3000000 + 4000000
+	//        <= 5000000 + 6000000 + 7000000 + 8000000;
 	public void testBreakingPrecedence() throws Exception {
 		fOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, CCorePlugin.SPACE);
 		assertFormatterResult();
@@ -2060,19 +2042,19 @@ public class CodeFormatterTest extends BaseUITestCase {
 	//    }
 	//    z();
 	//}
-	public void testBinaryExpressionInMacro_1() throws Exception {
+	public void testBinaryExpressionInMacro() throws Exception {
 		fOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, CCorePlugin.SPACE);
 		assertFormatterResult();
 	}
 
 	//class Stream {
-	//Stream& operator <<(const char* s);
+	//Stream& operator<<(const char* s);
 	//};
 	//
 	//class Voidifier {
 	//public:
 	//Voidifier();
-	//void operator &(Stream&);
+	//void operator&(Stream&);
 	//};
 	//
 	//Stream stream;
@@ -2083,13 +2065,13 @@ public class CodeFormatterTest extends BaseUITestCase {
 	//}
 
 	//class Stream {
-	//    Stream& operator <<(const char* s);
+	//    Stream& operator<<(const char* s);
 	//};
 	//
 	//class Voidifier {
 	//public:
 	//    Voidifier();
-	//    void operator &(Stream&);
+	//    void operator&(Stream&);
 	//};
 	//
 	//Stream stream;
@@ -2099,11 +2081,54 @@ public class CodeFormatterTest extends BaseUITestCase {
 	//    STREAM << "text text test text " << "text text "
 	//            << "text text text text te";
 	//}
-	public void testBinaryExpressionInMacro_2() throws Exception {
+	public void testOverloadedLeftShiftChain_1() throws Exception {
 		fOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, CCorePlugin.SPACE);
 		assertFormatterResult();
 	}
 
+	//class Stream {
+	//Stream& operator<<(const char* s);
+	//};
+	//
+	//class Voidifier {
+	//public:
+	//Voidifier();
+	//void operator&(Stream&);
+	//};
+	//
+	//Stream stream;
+	//#define STREAM Voidifier() & stream
+	//
+	//void test() {
+	//STREAM << "text text test text " << "text text text text text " << "text" <<
+	//"text text text text " << "text text text text text " << "text te";
+	//}
+
+	//class Stream {
+	//    Stream& operator<<(const char* s);
+	//};
+	//
+	//class Voidifier {
+	//public:
+	//    Voidifier();
+	//    void operator&(Stream&);
+	//};
+	//
+	//Stream stream;
+	//#define STREAM Voidifier() & stream
+	//
+	//void test() {
+	//    STREAM << "text text test text " << "text text text text text " << "text"
+	//           << "text text text text " << "text text text text text "
+	//           << "text te";
+	//}
+	public void testOverloadedLeftShiftChain_2() throws Exception {
+		fOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, CCorePlugin.SPACE);
+		fOptions.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_OVERLOADED_LEFT_SHIFT_CHAIN,
+				Integer.toString(Alignment.M_COMPACT_SPLIT | Alignment.M_INDENT_ON_COLUMN));
+		assertFormatterResult();
+	}
+
 	//int main() {
 	//	std::vector> test;
 	//	// some comment
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java
index 83342075755..f03873769dc 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java
@@ -199,9 +199,10 @@ final class FormatterMessages extends NLS {
 	public static String LineWrappingTabPage_enum_decls;
 	public static String LineWrappingTabPage_enumerator_list;
 	public static String LineWrappingTabPage_initializer_list;
-	public static String LineWrappingTabPage_conditionals;
 	public static String LineWrappingTabPage_binary_exprs;
+	public static String LineWrappingTabPage_conditionals;
 	public static String LineWrappingTabPage_member_access;
+	public static String LineWrappingTabPage_stream_output;
 	public static String LineWrappingTabPage_indentation_default;
 	public static String LineWrappingTabPage_indentation_on_column;
 	public static String LineWrappingTabPage_indentation_by_one;
@@ -221,9 +222,10 @@ final class FormatterMessages extends NLS {
 	public static String LineWrappingTabPage_enum_decls_lowercase;
 	public static String LineWrappingTabPage_enumerator_list_lowercase;
 	public static String LineWrappingTabPage_initializer_list_lowercase;
-	public static String LineWrappingTabPage_conditionals_lowercase;
 	public static String LineWrappingTabPage_binary_exprs_lowercase;
+	public static String LineWrappingTabPage_conditionals_lowercase;
 	public static String LineWrappingTabPage_member_access_lowercase;
+	public static String LineWrappingTabPage_stream_output_lowercase;
 	public static String LineWrappingTabPage_indentation_default_lowercase;
 	public static String LineWrappingTabPage_indentation_on_column_lowercase;
 	public static String LineWrappingTabPage_indentation_by_one_lowercase;
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties
index 4627de3fb0d..aca875578c4 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties
@@ -230,9 +230,10 @@ LineWrappingTabPage_constructor_initializer_list=Constructor initializer list
 LineWrappingTabPage_enum_decls='enum' declaration
 LineWrappingTabPage_enumerator_list=Enumerator list
 LineWrappingTabPage_initializer_list=Initializer list
-LineWrappingTabPage_conditionals=Conditionals
 LineWrappingTabPage_binary_exprs=Binary expressions
+LineWrappingTabPage_conditionals=Conditionals
 LineWrappingTabPage_member_access=Member access
+LineWrappingTabPage_stream_output=Stream output
 LineWrappingTabPage_indentation_default=Default indentation
 LineWrappingTabPage_indentation_on_column=Indent on column
 LineWrappingTabPage_indentation_by_one=Indent by one
@@ -254,9 +255,10 @@ LineWrappingTabPage_constructor_initializer_list_lowercase=constructor initializ
 LineWrappingTabPage_enum_decls_lowercase='enum' declaration
 LineWrappingTabPage_enumerator_list_lowercase=enumerator list
 LineWrappingTabPage_initializer_list_lowercase=initializer list
-LineWrappingTabPage_conditionals_lowercase=conditionals
 LineWrappingTabPage_binary_exprs_lowercase=binary expressions
+LineWrappingTabPage_conditionals_lowercase=conditionals
 LineWrappingTabPage_member_access_lowercase=member access
+LineWrappingTabPage_stream_output_lowercase=stream output
 LineWrappingTabPage_indentation_default_lowercase=default indentation
 LineWrappingTabPage_indentation_on_column_lowercase=indent on column
 LineWrappingTabPage_indentation_by_one_lowercase=indent by one
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/LineWrappingTabPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/LineWrappingTabPage.java
index 3672e6e280f..0a18ce67d56 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/LineWrappingTabPage.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/LineWrappingTabPage.java
@@ -8,6 +8,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Anton Leherbauer (Wind River Systems)
+ *     Sergey Prigogin (Google)
  *******************************************************************************/
 package org.eclipse.cdt.internal.ui.preferences.formatter;
 
@@ -55,25 +56,31 @@ import org.eclipse.cdt.internal.corext.util.Messages;
 public class LineWrappingTabPage extends FormatterTabPage {
 
     /**
-     * Represents a line wrapping category. All members are final.
+     * Represents a line wrapping category.
      */
 	private final static class Category {
 		public final String key;
 		public final String name;
 		public final String previewText;
-		public final String description; //bug 235453: for categories' labels
+		public final String prologue;
+		public final String description; // bug 235453: for categories' labels
 		public final List children;
 
 		public int index;
 
-		public Category(String key, String previewText, String name, String description) {
+		public Category(String key, String prologue, String previewText, String name, String description) {
 			this.key= key;
 			this.name= name;
+			this.prologue = prologue;
 			this.previewText= previewText != null ? createPreviewHeader(name) + previewText : null;
 			this.description = description;
 			children= new ArrayList();
 		}
 		
+		public Category(String key, String previewText, String name, String description) {
+			this(key, null, previewText, name, description);
+		}
+		
 		/**
 		 * @param name Category name
 		 */
@@ -165,7 +172,7 @@ public class LineWrappingTabPage extends FormatterTabPage {
 				index= 1; // In order to select a category with preview initially
 			}
 			final Category category= fCategoriesList.get(index);
-			fCategoriesViewer.setSelection(new StructuredSelection(new Category[] {category}));
+			fCategoriesViewer.setSelection(new StructuredSelection(new Category[] { category }));
 		}
 
         public void doubleClick(DoubleClickEvent event) {
@@ -187,10 +194,10 @@ public class LineWrappingTabPage extends FormatterTabPage {
 	        fElements.clear();
 	        evaluateElements(selection.iterator());
 	        evaluateMaps(wrappingStyleMap, indentStyleMap, forceWrappingMap);
-	        setPreviewText(getPreviewText(wrappingStyleMap, indentStyleMap, forceWrappingMap));
+            evaluatePreviewText();
 	        refreshControls(wrappingStyleMap, indentStyleMap, forceWrappingMap);
 	    }
-	    
+
 	    public List getElements() {
 	        return fElements;
 	    }
@@ -206,32 +213,38 @@ public class LineWrappingTabPage extends FormatterTabPage {
             		if (value != null) {
             			if (!fElements.contains(category))
             				fElements.add(category);
-            		}
-            		else {
+            		} else {
             			evaluateElements(category.children.iterator());
             		}
             	}
             }
         }
 	    
-	    private void evaluateMaps(Map wrappingStyleMap, Map indentStyleMap, Map forceWrappingMap) {
-	        Iterator iterator= fElements.iterator();
-            while (iterator.hasNext()) {
-                insertIntoMap(wrappingStyleMap, indentStyleMap, forceWrappingMap, iterator.next());
+	    private void evaluateMaps(Map wrappingStyleMap, Map indentStyleMap,
+	    		Map forceWrappingMap) {
+            for (Category category : fElements) {
+                insertIntoMap(wrappingStyleMap, indentStyleMap, forceWrappingMap, category);
             }
 	    }
   
-        private String getPreviewText(Map wrappingMap, Map indentMap, Map forceMap) {
-            Iterator iterator= fElements.iterator();
-            String previewText= ""; //$NON-NLS-1$
-            while (iterator.hasNext()) {
-                Category category= iterator.next();
-                previewText= previewText + category.previewText + "\n\n"; //$NON-NLS-1$
+		private void evaluatePreviewText() {
+			StringBuilder previewText = new StringBuilder();
+            for (Category category : fElements) {
+            	if (category.prologue != null) {
+	                previewText.append(category.prologue);
+	                previewText.append("\n\n"); //$NON-NLS-1$
+            	}
             }
-            return previewText;
-        }
-        
-        private void insertIntoMap(Map wrappingMap, Map indentMap, Map forceMap, Category category) {
+            int offset = previewText.length();
+            for (Category category : fElements) {
+                previewText.append(category.previewText);
+                previewText.append("\n\n"); //$NON-NLS-1$
+            }
+	        setPreviewText(previewText.toString(), offset);
+		}
+	    
+        private void insertIntoMap(Map wrappingMap, Map indentMap,
+        		Map forceMap, Category category) {
             final String value= fWorkingValues.get(category.key);
             Integer wrappingStyle;
             Integer indentStyle;
@@ -260,7 +273,8 @@ public class LineWrappingTabPage extends FormatterTabPage {
                 map.put(type, new Integer(count.intValue() + 1));
         }
                 
-        private void refreshControls(Map wrappingStyleMap, Map indentStyleMap, Map forceWrappingMap) {
+        private void refreshControls(Map wrappingStyleMap, Map indentStyleMap,
+        		Map forceWrappingMap) {
             updateCombos(wrappingStyleMap, indentStyleMap);
             updateButton(forceWrappingMap);
             Integer wrappingStyleMax= getWrappingStyleMax(wrappingStyleMap);
@@ -272,7 +286,7 @@ public class LineWrappingTabPage extends FormatterTabPage {
         
         private Integer getWrappingStyleMax(Map wrappingStyleMap) {
             int maxCount= 0, maxStyle= 0;
-            for (int i=0; i\n" + //$NON-NLS-1$
+		    "#include \n" + //$NON-NLS-1$
+		    "\n" + //$NON-NLS-1$
+		    "using namespace std;\n" + //$NON-NLS-1$
+		    "\n" + //$NON-NLS-1$
+		    "void PrintDate(int year, int month, int day) {" + //$NON-NLS-1$
+		    "cout << setfill('0') << setw(4) << year << '/' << setw(2) << month" + //$NON-NLS-1$
+		    " << '/' << setw(2) << day << endl;" + //$NON-NLS-1$
+		    "}", //$NON-NLS-1$
+		    FormatterMessages.LineWrappingTabPage_stream_output,
+	    	FormatterMessages.LineWrappingTabPage_stream_output
+		);
+
 //	private final Category fEnumConstArgumentsCategory= new Category(
 //	    	DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_ENUM_CONSTANT,
 //	    	"enum Example {" + //$NON-NLS-1$
@@ -522,7 +565,8 @@ public class LineWrappingTabPage extends FormatterTabPage {
 	/**
 	 * The key to save the user's preview window width in the dialog settings.
 	 */
-	private static final String PREF_PREVIEW_LINE_WIDTH= CUIPlugin.PLUGIN_ID + ".codeformatter.line_wrapping_tab_page.preview_line_width"; //$NON-NLS-1$
+	private static final String PREF_PREVIEW_LINE_WIDTH=
+			CUIPlugin.PLUGIN_ID + ".codeformatter.line_wrapping_tab_page.preview_line_width"; //$NON-NLS-1$
 	
 	/**
 	 * The dialog settings.
@@ -594,8 +638,8 @@ public class LineWrappingTabPage extends FormatterTabPage {
 	 * @return Create the categories tree.
 	 */
 	protected List createCategories() {
-
-		final Category classDeclarations= new Category(FormatterMessages.LineWrappingTabPage_class_decls,FormatterMessages.LineWrappingTabPage_class_decls_lowercase); 
+		final Category classDeclarations= new Category(FormatterMessages.LineWrappingTabPage_class_decls,
+				FormatterMessages.LineWrappingTabPage_class_decls_lowercase); 
 		classDeclarations.children.add(fTypeDeclarationBaseClauseCategory);
 		
 //		final Category constructorDeclarations= new Category(null, null, FormatterMessages.LineWrappingTabPage_constructor_decls); 
@@ -603,28 +647,33 @@ public class LineWrappingTabPage extends FormatterTabPage {
 //		constructorDeclarations.children.add(fConstructorThrowsClauseCategory);
 //		constructorDeclarations.children.add(fConstructorInitializerListCategory);
 
-		final Category methodDeclarations= new Category(null, null, FormatterMessages.LineWrappingTabPage_function_decls,FormatterMessages.LineWrappingTabPage_function_decls_lowercase); 
+		final Category methodDeclarations= new Category(null, null, FormatterMessages.LineWrappingTabPage_function_decls,
+				FormatterMessages.LineWrappingTabPage_function_decls_lowercase); 
 		methodDeclarations.children.add(fMethodDeclarationsParametersCategory);
 		methodDeclarations.children.add(fMethodThrowsClauseCategory);
 		methodDeclarations.children.add(fConstructorInitializerListCategory);
 
-		final Category enumDeclarations= new Category(FormatterMessages.LineWrappingTabPage_enum_decls,FormatterMessages.LineWrappingTabPage_enum_decls_lowercase); 
+		final Category enumDeclarations= new Category(FormatterMessages.LineWrappingTabPage_enum_decls,
+				FormatterMessages.LineWrappingTabPage_enum_decls_lowercase); 
 		enumDeclarations.children.add(fEnumeratorsCategory);
 //		enumDeclarations.children.add(fEnumDeclInterfacesCategory);
 //		enumDeclarations.children.add(fEnumConstArgumentsCategory);
 		
-		final Category functionCalls= new Category(FormatterMessages.LineWrappingTabPage_function_calls,FormatterMessages.LineWrappingTabPage_function_calls_lowercase); 
+		final Category functionCalls= new Category(FormatterMessages.LineWrappingTabPage_function_calls,
+				FormatterMessages.LineWrappingTabPage_function_calls_lowercase); 
 		functionCalls.children.add(fMessageSendArgumentsCategory);
 //		functionCalls.children.add(fMessageSendSelectorCategory);
 //		functionCalls.children.add(fExplicitConstructorArgumentsCategory);
 //		functionCalls.children.add(fAllocationExpressionArgumentsCategory);
 //		functionCalls.children.add(fQualifiedAllocationExpressionCategory);
 		
-		final Category expressions= new Category(FormatterMessages.LineWrappingTabPage_expressions,FormatterMessages.LineWrappingTabPage_expressions_lowercase); 
+		final Category expressions= new Category(FormatterMessages.LineWrappingTabPage_expressions,
+				FormatterMessages.LineWrappingTabPage_expressions_lowercase); 
 		expressions.children.add(fBinaryExpressionCategory);
 		expressions.children.add(fConditionalExpressionCategory);
-		expressions.children.add(fInitializerListExpressionsCategory);
 		expressions.children.add(fAssignmentCategory);
+		expressions.children.add(fInitializerListExpressionsCategory);
+		expressions.children.add(fStreamOutputExpressionCategory);
 		expressions.children.add(fMemberAccessExpressionCategory);
 		
 //		final Category statements= new Category(FormatterMessages.LineWrappingTabPage_statements); 
@@ -711,8 +760,9 @@ public class LineWrappingTabPage extends FormatterTabPage {
 	protected Composite doCreatePreviewPane(Composite composite, int numColumns) {
 		super.doCreatePreviewPane(composite, numColumns);
 		
-		final NumberPreference previewLineWidth= new NumberPreference(composite, numColumns / 2, fPreviewPreferences, LINE_SPLIT,
-		    0, 9999, FormatterMessages.LineWrappingTabPage_line_width_for_preview_label_text); 
+		final NumberPreference previewLineWidth= new NumberPreference(composite, numColumns / 2,
+				fPreviewPreferences, LINE_SPLIT, 0, 9999,
+				FormatterMessages.LineWrappingTabPage_line_width_for_preview_label_text); 
 		fDefaultFocusManager.add(previewLineWidth);
 		previewLineWidth.addObserver(fUpdater);
 		previewLineWidth.addObserver(new Observer() {
@@ -781,10 +831,14 @@ public class LineWrappingTabPage extends FormatterTabPage {
 		fWorkingValues.put(LINE_SPLIT, normalSetting);
 	}
 	
-	protected void setPreviewText(String text) {
+	 void setPreviewText(String text) {
+		setPreviewText(text, 0);
+	}
+
+	void setPreviewText(String text, int offset) {
 		final String normalSetting= fWorkingValues.get(LINE_SPLIT);
 		fWorkingValues.put(LINE_SPLIT, fPreviewPreferences.get(LINE_SPLIT));
-		fPreview.setPreviewText(text);
+		fPreview.setPreviewText(text, offset);
 		fWorkingValues.put(LINE_SPLIT, normalSetting);
 	}
 	
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/TranslationUnitPreview.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/TranslationUnitPreview.java
index eae27503e6a..816d08c5d22 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/TranslationUnitPreview.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/TranslationUnitPreview.java
@@ -32,6 +32,7 @@ import org.eclipse.cdt.internal.ui.ICStatusConstants;
 
 public class TranslationUnitPreview extends CPreview {
     private String fPreviewText;
+    private int fPreviewTextOffset;
 	private String fFormatterId;
 
     /**
@@ -50,6 +51,7 @@ public class TranslationUnitPreview extends CPreview {
         }
         fPreviewDocument.set(fPreviewText);
 		
+		fSourceViewer.setVisibleRegion(fPreviewTextOffset, fPreviewText.length() - fPreviewTextOffset);
 		fSourceViewer.setRedraw(false);
 		final IFormattingContext context = new FormattingContext();
 		try {
@@ -64,8 +66,9 @@ public class TranslationUnitPreview extends CPreview {
 				context.setProperty(FormattingContextProperties.CONTEXT_PREFERENCES, prefs);
 				context.setProperty(FormattingContextProperties.CONTEXT_DOCUMENT, Boolean.valueOf(true));
 				extension.format(fPreviewDocument, context);
-			} else
+			} else {
 				formatter.format(fPreviewDocument, new Region(0, fPreviewDocument.getLength()));
+			}
 		} catch (Exception e) {
 			final IStatus status= new Status(IStatus.ERROR, CUIPlugin.getPluginId(), ICStatusConstants.INTERNAL_ERROR, 
 				FormatterMessages.CPreview_formatter_exception, e); 
@@ -77,7 +80,12 @@ public class TranslationUnitPreview extends CPreview {
     }
     
     public void setPreviewText(String previewText) {
+		setPreviewText(previewText, 0);
+	}
+
+	public void setPreviewText(String previewText, int previewTextOffset) {
         fPreviewText= previewText;
+        fPreviewTextOffset = previewTextOffset;
         update();
     }