Bug 131292 - [format] align assignments in columns
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java
index 7640b4b..4784d9b 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java
@@ -14309,4 +14309,64 @@
 	String input = getCompilationUnit("Formatter", "", "test530756", "in.java").getSource();
 	formatSource(input, getCompilationUnit("Formatter", "", "test530756", "E_out.java").getSource());
 }
+
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292a() throws JavaModelException {
+	this.formatterPrefs.align_type_members_on_columns = true;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "A_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292b() throws JavaModelException {
+	this.formatterPrefs.align_variable_declarations_on_columns = true;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "B_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292c() throws JavaModelException {
+	this.formatterPrefs.align_assignment_statements_on_columns = true;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "C_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292d() throws JavaModelException {
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_variable_declarations_on_columns = true;
+	this.formatterPrefs.align_assignment_statements_on_columns = true;
+	this.formatterPrefs.tab_char = DefaultCodeFormatterOptions.SPACE;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "D_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292e() throws JavaModelException {
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_variable_declarations_on_columns = true;
+	this.formatterPrefs.align_assignment_statements_on_columns = true;
+	this.formatterPrefs.tab_char = DefaultCodeFormatterOptions.MIXED;
+	this.formatterPrefs.align_fields_grouping_blank_lines = 1;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "E_out.java").getSource());
+}
+/**
+ * https://bugs.eclipse.org/131292 - [format] align assignments in columns
+ */
+public void testBug131292f() throws JavaModelException {
+	this.formatterPrefs.align_type_members_on_columns = true;
+	this.formatterPrefs.align_variable_declarations_on_columns = true;
+	this.formatterPrefs.align_assignment_statements_on_columns = true;
+	this.formatterPrefs.align_with_spaces = true;
+	this.formatterPrefs.align_fields_grouping_blank_lines = 2;
+	String input = getCompilationUnit("Formatter", "", "test131292", "in.java").getSource();
+	formatSource(input, getCompilationUnit("Formatter", "", "test131292", "F_out.java").getSource());
+}
 }
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/A_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/A_out.java
new file mode 100644
index 0000000..ff98217
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/A_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a						b;
+	a						b				= c;
+	int						i;
+	int						j				= 5;
+	private String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object	someObjetc		= new Object() {
+												@Override
+												public String toString() {
+													return super.toString();
+												}
+											};
+
+	void variables() {
+		a b;
+		a b = c;
+		int i;
+		int j = 5;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesReordered() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		a b;
+	}
+
+	void variablesGaps() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+
+		// big gap
+
+		int i;
+		a b;
+	}
+
+	void variablesComments() {
+		a /* c1 */ b; // c1
+		a /* c123 */ b /* c123 */ = /* c123 */ c; // c123
+		int /* */ i; /* */
+		int j = 5; /* */
+		String /* c */ someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a b = c;
+		@Target
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int j = 5;
+		@Deprecated
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		@Test
+		a b;
+	}
+
+	void assignments() {
+		b = c;
+		j = 5;
+		myInteger = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void assignmentsReordered() {
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b = c;
+		myInteger = 56436345;
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+	}
+
+	void assignmentsGaps() {
+		b = c;
+		myInteger = 56436345;
+
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+
+		// big gap
+
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */ = /* c1 */c;// c1
+		j /* */ = /* c12345 */5; // c12345
+		myInteger /* */ = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890"; /* ... */
+		someObjetc = /* !!! */ new Object() { // !!!
+			@Override
+			public String toString() {
+				return super.toString(); // !!!
+			}
+		};
+	}
+
+	void mixed() {
+		a b;
+		a b = c;
+		int i;
+		j = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/B_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/B_out.java
new file mode 100644
index 0000000..cc1c279
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/B_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a b;
+	a b = c;
+	int i;
+	int j = 5;
+	private String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc = new Object() {
+		@Override
+		public String toString() {
+			return super.toString();
+		}
+	};
+
+	void variables() {
+		a				b;
+		a				b				= c;
+		int				i;
+		int				j				= 5;
+		String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object	someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+	}
+
+	void variablesReordered() {
+		a				b				= c;
+		String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int				j				= 5;
+		final Object	someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+		int				i;
+		a				b;
+	}
+
+	void variablesGaps() {
+		a				b				= c;
+		String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int				j				= 5;
+		final Object	someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+
+		// big gap
+
+		int				i;
+		a				b;
+	}
+
+	void variablesComments() {
+		a /* c1 */				b;																					// c1
+		a /* c123 */			b /* c123 */			= /* c123 */ c;												// c123
+		int /* */				i;																					/* */
+		int						j						= 5;														/* */
+		String /* c */			someLongString /* c */	= /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object	someObjetc				= new Object() {
+															@Override
+															public String toString() {
+																return super.toString();
+															}
+														};
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a				b				= c;
+		@Target
+		String			someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int				j				= 5;
+		@Deprecated
+		final Object	someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+		int				i;
+		@Test
+		a				b;
+	}
+
+	void assignments() {
+		b = c;
+		j = 5;
+		myInteger = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void assignmentsReordered() {
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b = c;
+		myInteger = 56436345;
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+	}
+
+	void assignmentsGaps() {
+		b = c;
+		myInteger = 56436345;
+
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+
+		// big gap
+
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */ = /* c1 */c;// c1
+		j /* */ = /* c12345 */5; // c12345
+		myInteger /* */ = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890"; /* ... */
+		someObjetc = /* !!! */ new Object() { // !!!
+			@Override
+			public String toString() {
+				return super.toString(); // !!!
+			}
+		};
+	}
+
+	void mixed() {
+		a	b;
+		a	b	= c;
+		int	i;
+		j = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/C_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/C_out.java
new file mode 100644
index 0000000..a84c6d0
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/C_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a b;
+	a b = c;
+	int i;
+	int j = 5;
+	private String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc = new Object() {
+		@Override
+		public String toString() {
+			return super.toString();
+		}
+	};
+
+	void variables() {
+		a b;
+		a b = c;
+		int i;
+		int j = 5;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesReordered() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		a b;
+	}
+
+	void variablesGaps() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+
+		// big gap
+
+		int i;
+		a b;
+	}
+
+	void variablesComments() {
+		a /* c1 */ b; // c1
+		a /* c123 */ b /* c123 */ = /* c123 */ c; // c123
+		int /* */ i; /* */
+		int j = 5; /* */
+		String /* c */ someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a b = c;
+		@Target
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int j = 5;
+		@Deprecated
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		@Test
+		a b;
+	}
+
+	void assignments() {
+		b				= c;
+		j				= 5;
+		myInteger		= 56436345;
+		myOtehrInteger	>>= 35534525543;
+		someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc		= new Object() {
+							@Override
+							public String toString() {
+								return super.toString();
+							}
+						};
+	}
+
+	void assignmentsReordered() {
+		someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b				= c;
+		myInteger		= 56436345;
+		someObjetc		= new Object() {
+							@Override
+							public String toString() {
+								return super.toString();
+							}
+						};
+		myOtehrInteger	>>= 35534525543;
+		j				= 5;
+	}
+
+	void assignmentsGaps() {
+		b				= c;
+		myInteger		= 56436345;
+
+		myOtehrInteger	>>= 35534525543;
+		j				= 5;
+
+		// big gap
+
+		someObjetc		= new Object() {
+							@Override
+							public String toString() {
+								return super.toString();
+							}
+						};
+		someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */		= /* c1 */c;																// c1
+		j /* */					= /* c12345 */5;															// c12345
+		myInteger /* */			= 56436345;
+		/* */ myOtehrInteger	>>= 35534525543;
+		someLongString			= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";																	/* ... */
+		someObjetc				= /* !!! */ new Object() {													// !!!
+									@Override
+									public String toString() {
+										return super.toString();											// !!!
+									}
+								};
+	}
+
+	void mixed() {
+		a b;
+		a b = c;
+		int i;
+		j				= 5;
+		someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/D_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/D_out.java
new file mode 100644
index 0000000..7358615
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/D_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+    a                    b;
+    a                    b              = c;
+    int                  i;
+    int                  j              = 5;
+    private String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+            + "12345678901234567890";
+    private final Object someObjetc     = new Object() {
+                                            @Override
+                                            public String toString() {
+                                                return super.toString();
+                                            }
+                                        };
+
+    void variables() {
+        a            b;
+        a            b              = c;
+        int          i;
+        int          j              = 5;
+        String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        final Object someObjetc     = new Object() {
+                                        @Override
+                                        public String toString() {
+                                            return super.toString();
+                                        }
+                                    };
+    }
+
+    void variablesReordered() {
+        a            b              = c;
+        String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        int          j              = 5;
+        final Object someObjetc     = new Object() {
+                                        @Override
+                                        public String toString() {
+                                            return super.toString();
+                                        }
+                                    };
+        int          i;
+        a            b;
+    }
+
+    void variablesGaps() {
+        a            b              = c;
+        String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+
+        int          j              = 5;
+        final Object someObjetc     = new Object() {
+                                        @Override
+                                        public String toString() {
+                                            return super.toString();
+                                        }
+                                    };
+
+        // big gap
+
+        int          i;
+        a            b;
+    }
+
+    void variablesComments() {
+        a /* c1 */             b;                                                                              // c1
+        a /* c123 */           b /* c123 */           = /* c123 */ c;                                          // c123
+        int /* */              i;                                                                              /* */
+        int                    j                      = 5;                                                     /* */
+        String /* c */         someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890" + "12345678901234567890";
+        /* ... */ final Object someObjetc             = new Object() {
+                                                          @Override
+                                                          public String toString() {
+                                                              return super.toString();
+                                                          }
+                                                      };
+    }
+
+    void variablesAnnotations() {
+        @SuppressWarnings
+        a            b              = c;
+        @Target
+        String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        int          j              = 5;
+        @Deprecated
+        final Object someObjetc     = new Object() {
+                                        @Override
+                                        public String toString() {
+                                            return super.toString();
+                                        }
+                                    };
+        int          i;
+        @Test
+        a            b;
+    }
+
+    void assignments() {
+        b                = c;
+        j                = 5;
+        myInteger        = 56436345;
+        myOtehrInteger >>= 35534525543;
+        someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        someObjetc       = new Object() {
+                             @Override
+                             public String toString() {
+                                 return super.toString();
+                             }
+                         };
+    }
+
+    void assignmentsReordered() {
+        someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        b                = c;
+        myInteger        = 56436345;
+        someObjetc       = new Object() {
+                             @Override
+                             public String toString() {
+                                 return super.toString();
+                             }
+                         };
+        myOtehrInteger >>= 35534525543;
+        j                = 5;
+    }
+
+    void assignmentsGaps() {
+        b                = c;
+        myInteger        = 56436345;
+
+        myOtehrInteger >>= 35534525543;
+        j                = 5;
+
+        // big gap
+
+        someObjetc       = new Object() {
+                             @Override
+                             public String toString() {
+                                 return super.toString();
+                             }
+                         };
+        someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+    }
+
+    void assignmentsComments() {
+        /* c1 */ b /* c1 */    = /* c1 */c;                                                              // c1
+        j /* */                = /* c12345 */5;                                                          // c12345
+        myInteger /* */        = 56436345;
+        /* */ myOtehrInteger >>= 35534525543;
+        someLongString         = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";                                                                /* ... */
+        someObjetc             = /* !!! */ new Object() {                                                // !!!
+                                   @Override
+                                   public String toString() {
+                                       return super.toString();                                          // !!!
+                                   }
+                               };
+    }
+
+    void mixed() {
+        a   b;
+        a   b = c;
+        int i;
+        j              = 5;
+        someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+                + "12345678901234567890";
+        final Object someObjetc = new Object() {
+            @Override
+            public String toString() {
+                return super.toString();
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/E_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/E_out.java
new file mode 100644
index 0000000..29b1900
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/E_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a					 b;
+	a					 b				= c;
+	int					 i;
+	int					 j				= 5;
+	private String		 someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc		= new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+
+	void variables() {
+		a			 b;
+		a			 b				= c;
+		int			 i;
+		int			 j				= 5;
+		String		 someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc		= new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+	}
+
+	void variablesReordered() {
+		a			 b				= c;
+		String		 someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int			 j				= 5;
+		final Object someObjetc		= new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+		int			 i;
+		a			 b;
+	}
+
+	void variablesGaps() {
+		a	   b			  = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int			 j			= 5;
+		final Object someObjetc	= new Object() {
+									@Override
+									public String toString() {
+										return super.toString();
+									}
+								};
+
+		// big gap
+
+		int	i;
+		a	b;
+	}
+
+	void variablesComments() {
+		a /* c1 */			   b;																			   // c1
+		a /* c123 */		   b /* c123 */			  = /* c123 */ c;										   // c123
+		int /* */			   i;																			   /* */
+		int					   j					  = 5;													   /* */
+		String /* c */		   someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc			  = new Object() {
+														  @Override
+														  public String toString() {
+															  return super.toString();
+														  }
+													  };
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a			 b				= c;
+		@Target
+		String		 someLongString	= "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int			 j				= 5;
+		@Deprecated
+		final Object someObjetc		= new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+		int			 i;
+		@Test
+		a			 b;
+	}
+
+	void assignments() {
+		b				 = c;
+		j				 = 5;
+		myInteger		 = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString	 = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc		 = new Object() {
+							 @Override
+							 public String toString() {
+								 return super.toString();
+							 }
+						 };
+	}
+
+	void assignmentsReordered() {
+		someLongString	 = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b				 = c;
+		myInteger		 = 56436345;
+		someObjetc		 = new Object() {
+							 @Override
+							 public String toString() {
+								 return super.toString();
+							 }
+						 };
+		myOtehrInteger >>= 35534525543;
+		j				 = 5;
+	}
+
+	void assignmentsGaps() {
+		b		  = c;
+		myInteger = 56436345;
+
+		myOtehrInteger >>= 35534525543;
+		j				 = 5;
+
+		// big gap
+
+		someObjetc	   = new Object() {
+						   @Override
+						   public String toString() {
+							   return super.toString();
+						   }
+					   };
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */	   = /* c1 */c;																 // c1
+		j /* */				   = /* c12345 */5;															 // c12345
+		myInteger /* */		   = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString		   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";																 /* ... */
+		someObjetc			   = /* !!! */ new Object() {												 // !!!
+								   @Override
+								   public String toString() {
+									   return super.toString();											 // !!!
+								   }
+							   };
+	}
+
+	void mixed() {
+		a	b;
+		a	b = c;
+		int	i;
+		j			   = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/F_out.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/F_out.java
new file mode 100644
index 0000000..8f70457
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/F_out.java
@@ -0,0 +1,177 @@
+class Example {
+
+	a                    b;
+	a                    b              = c;
+	int                  i;
+	int                  j              = 5;
+	private String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc     = new Object() {
+											@Override
+											public String toString() {
+												return super.toString();
+											}
+										};
+
+	void variables() {
+		a            b;
+		a            b              = c;
+		int          i;
+		int          j              = 5;
+		String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc     = new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+	}
+
+	void variablesReordered() {
+		a            b              = c;
+		String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int          j              = 5;
+		final Object someObjetc     = new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+		int          i;
+		a            b;
+	}
+
+	void variablesGaps() {
+		a            b              = c;
+		String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+
+		int          j              = 5;
+		final Object someObjetc     = new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+
+		// big gap
+
+		int i;
+		a   b;
+	}
+
+	void variablesComments() {
+		a /* c1 */             b;                                                                              // c1
+		a /* c123 */           b /* c123 */           = /* c123 */ c;                                          // c123
+		int /* */              i;                                                                              /* */
+		int                    j                      = 5;                                                     /* */
+		String /* c */         someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc             = new Object() {
+															@Override
+															public String toString() {
+																return super.toString();
+															}
+														};
+	}
+
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a            b              = c;
+		@Target
+		String       someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		int          j              = 5;
+		@Deprecated
+		final Object someObjetc     = new Object() {
+										@Override
+										public String toString() {
+											return super.toString();
+										}
+									};
+		int          i;
+		@Test
+		a            b;
+	}
+
+	void assignments() {
+		b                = c;
+		j                = 5;
+		myInteger        = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		someObjetc       = new Object() {
+								@Override
+								public String toString() {
+									return super.toString();
+								}
+							};
+	}
+
+	void assignmentsReordered() {
+		someLongString   = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		b                = c;
+		myInteger        = 56436345;
+		someObjetc       = new Object() {
+								@Override
+								public String toString() {
+									return super.toString();
+								}
+							};
+		myOtehrInteger >>= 35534525543;
+		j                = 5;
+	}
+
+	void assignmentsGaps() {
+		b                = c;
+		myInteger        = 56436345;
+
+		myOtehrInteger >>= 35534525543;
+		j                = 5;
+
+		// big gap
+
+		someObjetc     = new Object() {
+							@Override
+							public String toString() {
+								return super.toString();
+							}
+						};
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+	}
+
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */    = /* c1 */c;                                                              // c1
+		j /* */                = /* c12345 */5;                                                          // c12345
+		myInteger /* */        = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString         = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";                                                                /* ... */
+		someObjetc             = /* !!! */ new Object() {                                                // !!!
+									@Override
+									public String toString() {
+										return super.toString();                                         // !!!
+									}
+								};
+	}
+
+	void mixed() {
+		a   b;
+		a   b = c;
+		int i;
+		j              = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890"
+				+ "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/in.java b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/in.java
new file mode 100644
index 0000000..58f3b88
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Formatter/test131292/in.java
@@ -0,0 +1,166 @@
+class Example {
+
+	a b;
+	a b = c;
+	int i;
+	int j = 5;
+	private String someLongString = "12345678901234567890"
+			+ "12345678901234567890" + "12345678901234567890"
+			+ "12345678901234567890";
+	private final Object someObjetc = new Object() {
+		@Override
+		public String toString() {
+			return super.toString();
+		}
+	};
+
+	void variables() {
+		a b;
+		a b = c;
+		int i;
+		int j = 5;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void variablesReordered() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		a b;
+	}
+
+	void variablesGaps() {
+		a b = c;
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		
+		int j = 5;
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		
+		// big gap
+		
+		int i;
+		a b;
+	}
+
+	void variablesComments() {
+		a /* c1 */ b; // c1
+		a /* c123 */b /* c123 */ = /* c123 */ c; // c123
+		int /* */i; /* */
+		int j = 5; /* */
+		String /* c */ someLongString /* c */ = /* c */ "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		/* ... */ final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+	
+	void variablesAnnotations() {
+		@SuppressWarnings
+		a b = c;
+		@Target
+		String someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		int j = 5;
+		@Deprecated
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		int i;
+		@Test
+		a b;
+	}
+
+	void assignments() {
+		b = c;
+		j = 5;
+		myInteger = 56436345;
+		myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+
+	void assignmentsReordered() {
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		b = c;
+		myInteger = 56436345;
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+	}
+	void assignmentsGaps() {
+		b = c;
+		myInteger = 56436345;
+		
+		myOtehrInteger >>= 35534525543;
+		j = 5;
+
+		// big gap
+
+		someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+	}
+	void assignmentsComments() {
+		/* c1 */ b /* c1 */= /* c1 */c;// c1
+		j /* */= /* c12345 */5; // c12345
+		myInteger /* */ = 56436345;
+		/* */ myOtehrInteger >>= 35534525543;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890"; /* ... */
+		someObjetc = /* !!! */ new Object() { // !!!
+			@Override
+			public String toString() {
+				return super.toString(); // !!!
+			}
+		};
+	}
+
+	void mixed() {
+		a b;
+		a b = c;
+		int i;
+		j = 5;
+		someLongString = "12345678901234567890" + "12345678901234567890" + "12345678901234567890" + "12345678901234567890";
+		final Object someObjetc = new Object() {
+			@Override
+			public String toString() {
+				return super.toString();
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
index 1bf0cd4..92f38d6 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
@@ -70,6 +70,32 @@
 
 	/**
 	 * <pre>
+	 * FORMATTER / Option to align variable declarations on column
+	 *     - option id:         "org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns"
+	 *     - possible values:   { TRUE, FALSE }
+	 *     - default:           FALSE
+	 * </pre>
+	 * @see #TRUE
+	 * @see #FALSE
+	 * @since 3.15
+	 */
+	public static final String FORMATTER_ALIGN_VARIABLE_DECLARATIONS_ON_COLUMNS = JavaCore.PLUGIN_ID + ".formatter.align_variable_declarations_on_columns";	 //$NON-NLS-1$
+	
+	/**
+	 * <pre>
+	 * FORMATTER / Option to align assignment statements on column
+	 *     - option id:         "org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns"
+	 *     - possible values:   { TRUE, FALSE }
+	 *     - default:           FALSE
+	 * </pre>
+	 * @see #TRUE
+	 * @see #FALSE
+	 * @since 3.15
+	 */
+	public static final String FORMATTER_ALIGN_ASSIGNMENT_STATEMENTS_ON_COLUMNS = JavaCore.PLUGIN_ID + ".formatter.align_assignment_statements_on_columns";	 //$NON-NLS-1$
+	
+	/**
+	 * <pre>
 	 * FORMATTER / Option to use spaces when aligning members, independent of selected tabulation character
 	 *     - option id:         "org.eclipse.jdt.core.formatter.align_with_spaces"
 	 *     - possible values:   { TRUE, FALSE }
@@ -83,7 +109,10 @@
 
 	/**
 	 * <pre>
-	 * FORMATTER / Option to align groups of members independently if they are separated by a certain number of blank lines
+	 * FORMATTER / Option to affect aligning on columns: groups of items are aligned independently
+	 * if they are separated by at least the selected number of blank lines.
+	 * Note: since 3.15 the 'fields' part is a (potentially misleading) residue as this option
+	 * affects other types of aligning on columns as well.
 	 *     - option id:         "org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines"
 	 *     - possible values:   "&lt;n&gt;", where n is a positive integer
 	 *     - default:           {@code Integer.MAX_VALUE}
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
index 5f416ea..92f65e9 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
@@ -143,6 +143,8 @@
 	public int alignment_for_union_type_in_multicatch;
 
 	public boolean align_type_members_on_columns;
+	public boolean align_variable_declarations_on_columns;
+	public boolean align_assignment_statements_on_columns;
 	public boolean align_with_spaces;
 	public int align_fields_grouping_blank_lines;
 
@@ -485,6 +487,8 @@
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_TYPE_PARAMETERS, getAlignment(this.alignment_for_type_parameters));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_UNION_TYPE_IN_MULTICATCH, getAlignment(this.alignment_for_union_type_in_multicatch));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_TYPE_MEMBERS_ON_COLUMNS, this.align_type_members_on_columns ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
+		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_VARIABLE_DECLARATIONS_ON_COLUMNS, this.align_variable_declarations_on_columns ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
+		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_ASSIGNMENT_STATEMENTS_ON_COLUMNS, this.align_assignment_statements_on_columns ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_FIELDS_GROUPING_BLANK_LINES, Integer.toString(this.align_fields_grouping_blank_lines));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_WITH_SPACES, this.align_with_spaces ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ANNOTATION_TYPE_DECLARATION, this.brace_position_for_annotation_type_declaration);
@@ -1047,6 +1051,14 @@
 		if (alignTypeMembersOnColumnsOption != null) {
 			this.align_type_members_on_columns = DefaultCodeFormatterConstants.TRUE.equals(alignTypeMembersOnColumnsOption);
 		}
+		final Object alignVariableDeclarationsOnColumnsOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGN_VARIABLE_DECLARATIONS_ON_COLUMNS);
+		if (alignVariableDeclarationsOnColumnsOption != null) {
+			this.align_variable_declarations_on_columns = DefaultCodeFormatterConstants.TRUE.equals(alignVariableDeclarationsOnColumnsOption);
+		}
+		final Object alignAssignmentStatementsOnColumnsOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGN_ASSIGNMENT_STATEMENTS_ON_COLUMNS);
+		if (alignAssignmentStatementsOnColumnsOption != null) {
+			this.align_assignment_statements_on_columns = DefaultCodeFormatterConstants.TRUE.equals(alignAssignmentStatementsOnColumnsOption);
+		}
 		final Object alignGroupSepartionBlankLinesOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGN_FIELDS_GROUPING_BLANK_LINES);
 		if (alignTypeMembersOnColumnsOption != null) {
 			try {
@@ -2495,6 +2507,8 @@
 		this.alignment_for_type_parameters = Alignment.M_NO_ALIGNMENT;
 		this.alignment_for_union_type_in_multicatch = Alignment.M_COMPACT_SPLIT;
 		this.align_type_members_on_columns = false;
+		this.align_variable_declarations_on_columns = false;
+		this.align_assignment_statements_on_columns = false;
 		this.align_with_spaces = false;
 		this.align_fields_grouping_blank_lines = Integer.MAX_VALUE;
 		this.brace_position_for_annotation_type_declaration = DefaultCodeFormatterConstants.END_OF_LINE;
@@ -2813,6 +2827,8 @@
 		this.alignment_for_type_parameters = Alignment.M_NO_ALIGNMENT;
 		this.alignment_for_union_type_in_multicatch = Alignment.M_COMPACT_SPLIT;
 		this.align_type_members_on_columns = false;
+		this.align_variable_declarations_on_columns = false;
+		this.align_assignment_statements_on_columns = false;
 		this.align_with_spaces = false;
 		this.align_fields_grouping_blank_lines = Integer.MAX_VALUE;
 		this.brace_position_for_annotation_type_declaration = DefaultCodeFormatterConstants.END_OF_LINE;
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/Aligner.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/Aligner.java
new file mode 100644
index 0000000..47d3357
--- /dev/null
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/Aligner.java
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 2018 Mateusz Matela and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Mateusz Matela <mateusz.matela@gmail.com> - [formatter] Formatter does not format Java code correctly, especially when max line width is set - https://bugs.eclipse.org/303519
+ *     Lars Vogel <Lars.Vogel@vogella.com> - Contributions for
+ *     						Bug 473178
+ *******************************************************************************/
+package org.eclipse.jdt.internal.formatter.linewrap;
+
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameEQUAL;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameIdentifier;
+import static java.util.stream.Collectors.toList;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
+import org.eclipse.jdt.internal.formatter.Token;
+import org.eclipse.jdt.internal.formatter.TokenManager;
+import org.eclipse.jdt.internal.formatter.TokenTraverser;
+
+/** Implementation of the "Align items on columns" feature */
+public class Aligner {
+	private class PositionCounter extends TokenTraverser {
+		int stoppingIndex;
+		int maxPosition;
+
+		public PositionCounter() {
+			// nothing to do
+		}
+
+		@Override
+		protected boolean token(Token token, int index) {
+			if (index == this.stoppingIndex)
+				return false;
+			if (getLineBreaksBefore() > 0)
+				this.counter = Aligner.this.tm.getPositionInLine(index);
+			if (token.getAlign() > 0)
+				this.counter = token.getAlign();
+			this.counter += Aligner.this.tm.getLength(token, this.counter);
+			if (isSpaceAfter() && getLineBreaksAfter() == 0)
+				this.counter++;
+			this.maxPosition = Math.max(this.maxPosition, this.counter);
+			return true;
+		}
+
+		public int findMaxPosition(int fromIndex, int toIndex) {
+			this.counter = Aligner.this.tm.getPositionInLine(fromIndex);
+			this.stoppingIndex = toIndex;
+			this.maxPosition = 0;
+			Aligner.this.tm.traverse(fromIndex, this);
+			return this.maxPosition;
+		}
+	}
+
+	@FunctionalInterface
+	private interface AlignIndexFinder<N extends ASTNode> {
+		Optional<Integer> findIndex(N node);
+	}
+
+	private final List<List<? extends ASTNode>> alignGroups = new ArrayList<>();
+
+	private final DefaultCodeFormatterOptions options;
+
+	final TokenManager tm;
+
+	public Aligner(TokenManager tokenManager, DefaultCodeFormatterOptions options) {
+		this.tm = tokenManager;
+		this.options = options;
+	}
+
+	public void handleAlign(List<BodyDeclaration> bodyDeclarations) {
+		if (!this.options.align_type_members_on_columns)
+			return;
+		List<List<FieldDeclaration>> fieldGroups = toAlignGroups(bodyDeclarations,
+				n -> optionalCast(n, FieldDeclaration.class));
+		this.alignGroups.addAll(fieldGroups);
+
+		AlignIndexFinder<FieldDeclaration> nameFinder = fd -> findName(
+				(VariableDeclarationFragment) fd.fragments().get(0));
+		fieldGroups.forEach(fg -> alignNodes(fg, nameFinder));
+
+		AlignIndexFinder<FieldDeclaration> assignFinder = fd -> findAssign(
+				(VariableDeclarationFragment) fd.fragments().get(0));
+		fieldGroups.forEach(fg -> alignNodes(fg, assignFinder));
+	}
+
+	public void handleAlign(Block block) {
+		List<Statement> statements = block.statements();
+		if (this.options.align_variable_declarations_on_columns)
+			alignDeclarations(statements);
+		if (this.options.align_assignment_statements_on_columns)
+			alignAssignmentStatements(statements);
+	}
+
+	private void alignDeclarations(List<Statement> statements) {
+		List<List<VariableDeclarationStatement>> variableGroups = toAlignGroups(statements,
+				n -> optionalCast(n, VariableDeclarationStatement.class));
+		this.alignGroups.addAll(variableGroups);
+
+		AlignIndexFinder<VariableDeclarationStatement> nameFinder = vd -> findName(
+				(VariableDeclarationFragment) vd.fragments().get(0));
+		variableGroups.forEach(vg -> alignNodes(vg, nameFinder));
+
+		AlignIndexFinder<VariableDeclarationStatement> assignFinder = vd -> findAssign(
+				(VariableDeclarationFragment) vd.fragments().get(0));
+		variableGroups.forEach(vg -> alignNodes(vg, assignFinder));
+	}
+
+	private void alignAssignmentStatements(List<Statement> statements) {
+		List<List<ExpressionStatement>> assignmentGroups = toAlignGroups(statements,
+				n -> optionalCast(n, ExpressionStatement.class)
+						.filter(es -> es.getExpression() instanceof Assignment));
+		this.alignGroups.addAll(assignmentGroups);
+
+		AlignIndexFinder<ExpressionStatement> assignFinder = es -> {
+			Assignment a = (Assignment) es.getExpression();
+			int operatorIndex = this.tm.firstIndexBefore(a.getRightHandSide(), -1);
+			while (this.tm.get(operatorIndex).isComment())
+				operatorIndex--;
+			return Optional.of(operatorIndex);
+		};
+		assignmentGroups.forEach(ag -> alignNodes(ag, assignFinder));
+
+		if (this.options.align_with_spaces || this.options.tab_char != DefaultCodeFormatterOptions.TAB) {
+			// align assign operators on their right side (e.g. +=, >>=)
+			for (List<ExpressionStatement> group : assignmentGroups) {
+				List<Token> assignTokens = group.stream()
+						.map(assignFinder::findIndex)
+						.filter(Optional::isPresent)
+						.map(o -> this.tm.get(o.get()))
+						.collect(toList());
+				int maxWidth = assignTokens.stream().mapToInt(Token::countChars).max().orElse(0);
+				for (Token token : assignTokens)
+					token.setAlign(token.getAlign() + maxWidth - token.countChars());
+			}
+		}
+	}
+
+	private <N extends ASTNode> Optional<N> optionalCast(ASTNode node, Class<N> c) {
+		return Optional.of(node).filter(c::isInstance).map(c::cast);
+	}
+
+	private Optional<Integer> findName(VariableDeclarationFragment fragment) {
+		int nameIndex = this.tm.firstIndexIn(fragment.getName(), TokenNameIdentifier);
+		return Optional.of(nameIndex);
+	}
+
+	private Optional<Integer> findAssign(VariableDeclarationFragment fragment) {
+		return Optional.ofNullable(fragment.getInitializer())
+				.map(i -> this.tm.firstIndexBefore(i, TokenNameEQUAL));
+	}
+
+	private <N extends ASTNode> List<List<N>> toAlignGroups(List<? extends ASTNode> nodes,
+			Function<ASTNode, Optional<N>> nodeConverter) {
+		List<List<N>> result = new ArrayList<>();
+		List<N> alignGroup = new ArrayList<>();
+		N previous = null;
+		for (ASTNode node : nodes) {
+			Optional<N> converted = nodeConverter.apply(node);
+			if (converted.isPresent()) {
+				if (isNewGroup(node, previous)) {
+					result.add(alignGroup);
+					alignGroup = new ArrayList<>();
+				}
+				alignGroup.add(converted.get());
+			}
+			previous = converted.orElse(null);
+		}
+		result.add(alignGroup);
+		result.removeIf(l -> l.size() < 2);
+		return result;
+	}
+
+	private boolean isNewGroup(ASTNode node, ASTNode previousNode) {
+		if (previousNode == null)
+			return true;
+		int lineBreaks = 0;
+		int from = this.tm.lastIndexIn(previousNode, -1);
+		int to = this.tm.firstIndexIn(node, -1);
+		Token previousToken = this.tm.get(from);
+		for (int i = from + 1; i <= to; i++) {
+			Token token = this.tm.get(i);
+			lineBreaks += Math.min(this.tm.countLineBreaksBetween(previousToken, token),
+					this.options.number_of_empty_lines_to_preserve + 1);
+			previousToken = token;
+		}
+		return lineBreaks > this.options.align_fields_grouping_blank_lines;
+	}
+
+	private <N extends ASTNode> void alignNodes(List<N> alignGroup, AlignIndexFinder<N> tokenFinder) {
+		int[] tokenIndexes = alignGroup.stream()
+				.map(tokenFinder::findIndex)
+				.filter(Optional::isPresent)
+				.mapToInt(Optional::get).toArray();
+		OptionalInt maxPosition = IntStream.of(tokenIndexes).map(this.tm::getPositionInLine).max();
+		if (maxPosition.isPresent()) {
+			int align = normalizedAlign(maxPosition.getAsInt());
+			for (int tokenIndex : tokenIndexes)
+				this.tm.get(tokenIndex).setAlign(align);
+		}
+	}
+
+	public void alignComments() {
+		boolean alignLineComments = !this.options.comment_preserve_white_space_between_code_and_line_comments;
+		PositionCounter positionCounter = new PositionCounter();
+		// align comments after field declarations
+		for (List<? extends ASTNode> alignGroup : this.alignGroups) {
+			int maxCommentAlign = 0;
+			for (ASTNode node : alignGroup) {
+				int firstIndexInLine = findFirstTokenInLine(node);
+				int lastIndex = this.tm.lastIndexIn(node, -1) + 1;
+				maxCommentAlign = Math.max(maxCommentAlign,
+						positionCounter.findMaxPosition(firstIndexInLine, lastIndex));
+			}
+			maxCommentAlign = normalizedAlign(maxCommentAlign);
+
+			for (ASTNode node : alignGroup) {
+				int firstIndexInLine = findFirstTokenInLine(node);
+				int lastIndex = this.tm.lastIndexIn(node, -1);
+				lastIndex = Math.min(lastIndex, this.tm.size() - 2);
+				for (int i = firstIndexInLine; i <= lastIndex; i++) {
+					Token token = this.tm.get(i);
+					Token next = this.tm.get(i + 1);
+					boolean lineBreak = token.getLineBreaksAfter() > 0 || next.getLineBreaksBefore() > 0;
+					if (lineBreak) {
+						if (token.tokenType == TokenNameCOMMENT_BLOCK) {
+							token.setAlign(maxCommentAlign);
+						} else if (alignLineComments) {
+							this.tm.addNLSAlignIndex(i, maxCommentAlign);
+						}
+					} else if (next.tokenType == TokenNameCOMMENT_LINE && alignLineComments
+							|| (next.tokenType == TokenNameCOMMENT_BLOCK && i == lastIndex)) {
+						next.setAlign(maxCommentAlign);
+					}
+				}
+			}
+		}
+	}
+
+	private int findFirstTokenInLine(ASTNode node) {
+		if (node instanceof FieldDeclaration) {
+			int typeIndex = this.tm.firstIndexIn(((FieldDeclaration) node).getType(), -1);
+			return this.tm.findFirstTokenInLine(typeIndex);
+		}
+		if (node instanceof VariableDeclarationStatement) {
+			int typeIndex = this.tm.firstIndexIn(((VariableDeclarationStatement) node).getType(), -1);
+			return this.tm.findFirstTokenInLine(typeIndex);
+		}
+		if (node instanceof ExpressionStatement) {
+			return this.tm.firstIndexIn(node, -1);
+		}
+		throw new IllegalArgumentException(node.getClass().getName());
+	}
+
+	private int normalizedAlign(int desiredAlign) {
+		if (this.options.align_with_spaces)
+			return desiredAlign;
+		return this.tm.toIndent(desiredAlign, false);
+	}
+}
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java
deleted file mode 100644
index 3e0863e..0000000
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014, 2016 Mateusz Matela and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Mateusz Matela <mateusz.matela@gmail.com> - [formatter] Formatter does not format Java code correctly, especially when max line width is set - https://bugs.eclipse.org/303519
- *     Lars Vogel <Lars.Vogel@vogella.com> - Contributions for
- *     						Bug 473178
- *******************************************************************************/
-package org.eclipse.jdt.internal.formatter.linewrap;
-
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK;
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE;
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameEQUAL;
-import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameIdentifier;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jdt.core.dom.BodyDeclaration;
-import org.eclipse.jdt.core.dom.FieldDeclaration;
-import org.eclipse.jdt.core.dom.SimpleName;
-import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
-import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
-import org.eclipse.jdt.internal.formatter.Token;
-import org.eclipse.jdt.internal.formatter.TokenManager;
-import org.eclipse.jdt.internal.formatter.TokenTraverser;
-
-/** Implementation of the "Align fields on columns" feature */
-public class FieldAligner {
-	private class PositionCounter extends TokenTraverser {
-		int stoppingIndex;
-		int maxPosition;
-
-		public PositionCounter() {
-			// nothing to do
-		}
-
-		@Override
-		protected boolean token(Token token, int index) {
-			if (index == this.stoppingIndex)
-				return false;
-			if (getLineBreaksBefore() > 0)
-				this.counter = FieldAligner.this.tm.getPositionInLine(index);
-			if (token.getAlign() > 0)
-				this.counter = token.getAlign();
-			this.counter += FieldAligner.this.tm.getLength(token, this.counter);
-			if (isSpaceAfter() && getLineBreaksAfter() == 0)
-				this.counter++;
-			this.maxPosition = Math.max(this.maxPosition, this.counter);
-			return true;
-		}
-
-		public int findMaxPosition(int fromIndex, int toIndex) {
-			this.counter = FieldAligner.this.tm.getPositionInLine(fromIndex);
-			this.stoppingIndex = toIndex;
-			this.maxPosition = 0;
-			FieldAligner.this.tm.traverse(fromIndex, this);
-			return this.maxPosition;
-		}
-	}
-
-	private final List<List<FieldDeclaration>> fieldAlignGroups = new ArrayList<>();
-
-	private final DefaultCodeFormatterOptions options;
-
-	final TokenManager tm;
-
-	public FieldAligner(TokenManager tokenManager, DefaultCodeFormatterOptions options) {
-		this.tm = tokenManager;
-		this.options = options;
-	}
-
-	public void handleAlign(List<FieldDeclaration> bodyDeclarations) {
-		if (!this.options.align_type_members_on_columns)
-			return;
-		ArrayList<FieldDeclaration> alignGroup = new ArrayList<>();
-		BodyDeclaration previous = null;
-		for (BodyDeclaration declaration : bodyDeclarations) {
-			if (declaration instanceof FieldDeclaration) {
-				if (isNewGroup(declaration, previous)) {
-					alignFields(alignGroup);
-					alignGroup = new ArrayList<>();
-				}
-				alignGroup.add((FieldDeclaration) declaration);
-			}
-			previous = declaration;
-		}
-		alignFields(alignGroup);
-	}
-
-	private boolean isNewGroup(BodyDeclaration declaration, BodyDeclaration previousDeclaration) {
-		if (!(previousDeclaration instanceof FieldDeclaration))
-			return true;
-		int lineBreaks = 0;
-		int from = this.tm.lastIndexIn(previousDeclaration, -1);
-		int to = this.tm.firstIndexIn(declaration, -1);
-		Token previous = this.tm.get(from);
-		for (int i = from + 1; i <= to; i++) {
-			Token token = this.tm.get(i);
-			lineBreaks += Math.min(this.tm.countLineBreaksBetween(previous, token),
-					this.options.number_of_empty_lines_to_preserve + 1);
-			previous = token;
-		}
-		return lineBreaks > this.options.align_fields_grouping_blank_lines;
-	}
-
-	private void alignFields(ArrayList<FieldDeclaration> alignGroup) {
-		if (alignGroup.size() < 2)
-			return;
-		this.fieldAlignGroups.add(alignGroup);
-
-		int maxNameAlign = 0;
-		for (FieldDeclaration declaration : alignGroup) {
-			List<VariableDeclarationFragment> fragments = declaration.fragments();
-			SimpleName fieldName = fragments.get(0).getName();
-			int nameIndex = this.tm.firstIndexIn(fieldName, TokenNameIdentifier);
-			int positionInLine = this.tm.getPositionInLine(nameIndex);
-			maxNameAlign = Math.max(maxNameAlign, positionInLine);
-		}
-		maxNameAlign = normalizedAlign(maxNameAlign);
-
-		int maxAssignAlign = 0;
-		for (FieldDeclaration declaration : alignGroup) {
-			List<VariableDeclarationFragment> fragments = declaration.fragments();
-			VariableDeclarationFragment fragment = fragments.get(0);
-			int nameIndex = this.tm.firstIndexIn(fragment.getName(), TokenNameIdentifier);
-			Token nameToken = this.tm.get(nameIndex);
-
-			nameToken.setAlign(maxNameAlign);
-
-			if (fragment.getInitializer() != null) {
-				int equalIndex = this.tm.firstIndexAfter(fragment.getName(), TokenNameEQUAL);
-				int positionInLine = this.tm.getPositionInLine(equalIndex);
-				maxAssignAlign = Math.max(maxAssignAlign, positionInLine);
-			}
-		}
-		maxAssignAlign = normalizedAlign(maxAssignAlign);
-
-		for (FieldDeclaration declaration : alignGroup) {
-			List<VariableDeclarationFragment> fragments = declaration.fragments();
-			VariableDeclarationFragment fragment = fragments.get(0);
-			if (fragment.getInitializer() != null) {
-				int assingIndex = this.tm.firstIndexAfter(fragment.getName(), TokenNameEQUAL);
-				Token assignToken = this.tm.get(assingIndex);
-				assignToken.setAlign(maxAssignAlign);
-			}
-		}
-	}
-
-	public void alignComments() {
-		if (this.fieldAlignGroups.isEmpty())
-			return;
-		boolean alignLineComments = !this.options.comment_preserve_white_space_between_code_and_line_comments;
-		PositionCounter positionCounter = new PositionCounter();
-		// align comments after field declarations
-		for (List<FieldDeclaration> alignGroup : this.fieldAlignGroups) {
-			int maxCommentAlign = 0;
-			for (FieldDeclaration declaration : alignGroup) {
-				int typeIndex = this.tm.firstIndexIn(declaration.getType(), -1);
-				int firstIndexInLine = this.tm.findFirstTokenInLine(typeIndex);
-				int lastIndex = this.tm.lastIndexIn(declaration, -1) + 1;
-				maxCommentAlign = Math.max(maxCommentAlign,
-						positionCounter.findMaxPosition(firstIndexInLine, lastIndex));
-			}
-			maxCommentAlign = normalizedAlign(maxCommentAlign);
-
-			for (FieldDeclaration declaration : alignGroup) {
-				int typeIndex = this.tm.firstIndexIn(declaration.getType(), -1);
-				int firstIndexInLine = this.tm.findFirstTokenInLine(typeIndex);
-				int lastIndex = this.tm.lastIndexIn(declaration, -1);
-				lastIndex = Math.min(lastIndex, this.tm.size() - 2);
-				for (int i = firstIndexInLine; i <= lastIndex; i++) {
-					Token token = this.tm.get(i);
-					Token next = this.tm.get(i + 1);
-					boolean lineBreak = token.getLineBreaksAfter() > 0 || next.getLineBreaksBefore() > 0;
-					if (lineBreak) {
-						if (token.tokenType == TokenNameCOMMENT_BLOCK) {
-							token.setAlign(maxCommentAlign);
-						} else if (alignLineComments) {
-							this.tm.addNLSAlignIndex(i, maxCommentAlign);
-						}
-					} else if (next.tokenType == TokenNameCOMMENT_LINE && alignLineComments
-							|| (next.tokenType == TokenNameCOMMENT_BLOCK && i == lastIndex)) {
-						next.setAlign(maxCommentAlign);
-					}
-				}
-			}
-		}
-	}
-
-	private int normalizedAlign(int desiredAlign) {
-		if (this.options.align_with_spaces)
-			return desiredAlign;
-		return this.tm.toIndent(desiredAlign, false);
-	}
-}
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
index 9987d6a..1b68433 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
@@ -175,7 +175,7 @@
 	final DefaultCodeFormatterOptions options;
 	final int kind;
 
-	final FieldAligner fieldAligner;
+	final Aligner aligner;
 
 	int importsStart = -1, importsEnd = -1;
 
@@ -197,7 +197,7 @@
 		this.options = options;
 		this.kind = kind;
 
-		this.fieldAligner = new FieldAligner(this.tm, this.options);
+		this.aligner = new Aligner(this.tm, this.options);
 	}
 
 	@Override
@@ -258,20 +258,20 @@
 		prepareElementsList(node.typeParameters(), TokenNameCOMMA, TokenNameLESS);
 		handleWrap(this.options.alignment_for_type_parameters);
 
-		this.fieldAligner.handleAlign(node.bodyDeclarations());
+		this.aligner.handleAlign(node.bodyDeclarations());
 
 		return true;
 	}
 
 	@Override
 	public boolean visit(AnnotationTypeDeclaration node) {
-		this.fieldAligner.handleAlign(node.bodyDeclarations());
+		this.aligner.handleAlign(node.bodyDeclarations());
 		return true;
 	}
 
 	@Override
 	public boolean visit(AnonymousClassDeclaration node) {
-		this.fieldAligner.handleAlign(node.bodyDeclarations());
+		this.aligner.handleAlign(node.bodyDeclarations());
 		return true;
 	}
 
@@ -367,7 +367,7 @@
 			handleWrap(this.options.alignment_for_superinterfaces_in_enum_declaration, PREFERRED);
 		}
 
-		this.fieldAligner.handleAlign(node.bodyDeclarations());
+		this.aligner.handleAlign(node.bodyDeclarations());
 
 		return true;
 	}
@@ -383,6 +383,12 @@
 	}
 
 	@Override
+	public boolean visit(Block node) {
+		this.aligner.handleAlign(node);
+		return true;
+	}
+
+	@Override
 	public boolean visit(MethodInvocation node) {
 		handleArguments(node.arguments(), this.options.alignment_for_arguments_in_method_invocation);
 		handleTypeArguments(node.typeArguments());
@@ -1075,7 +1081,7 @@
 		preserveExistingLineBreaks();
 		applyBreaksOutsideRegions(regions);
 		new WrapExecutor(this.tm, this.options).executeWraps();
-		this.fieldAligner.alignComments();
+		this.aligner.alignComments();
 		wrapComments();
 		fixEnumConstantIndents(astRoot);
 	}