Repository: sqlfluff/sqlfluff

Issue:
Change L007 to specify end of line as well as beginning of line consistency
L007 enforces "Operators near newlines should be after, not before the newline."

That is this:

```sql
SELECT
    a
    + b
FROM foo
```

is preferred over this:

```sql
SELECT
    a +
    b
FROM foo
```

I'd like to suggest an `operator-before-end-of-line` config param (defaulted to `False` for backwards compatibility) which can be used the consistently enforce the latter behaviour for teams that prefer that.

If accepted, we should reword this rule to "Operators near newlines should be consistent in regards to placement before or after the newline (default is to prefer before)."

Happy to look at this one myself.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f0db244f..cd7b7192e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 
 - Support for primary index name, collect stats improvement, COMMENT statement for teradata dialect [#] (https://github.com/sqlfluff/sqlfluff/issues/)
+- Support config for L007 to prefer end of line operators [#1261](https://github.com/sqlfluff/sqlfluff/issues/1261)
 
 ### Changed
 
diff --git a/src/sqlfluff/core/default_config.cfg b/src/sqlfluff/core/default_config.cfg
index fb514f5b2..87290b284 100644
--- a/src/sqlfluff/core/default_config.cfg
+++ b/src/sqlfluff/core/default_config.cfg
@@ -48,6 +48,9 @@ unquoted_identifiers_policy = all
 [sqlfluff:rules:L003]
 lint_templated_tokens = True
 
+[sqlfluff:rules:L007]  # Keywords
+operator_new_lines = after
+
 [sqlfluff:rules:L010]  # Keywords
 capitalisation_policy = consistent
 
diff --git a/src/sqlfluff/core/rules/config_info.py b/src/sqlfluff/core/rules/config_info.py
index 78fedcd3f..89c0307ff 100644
--- a/src/sqlfluff/core/rules/config_info.py
+++ b/src/sqlfluff/core/rules/config_info.py
@@ -86,6 +86,10 @@ STANDARD_CONFIG_INFO_DICT = {
         "validation": [True, False],
         "definition": ("Should count(1) be preferred over count(*)?"),
     },
+    "operator_new_lines": {
+        "validation": ["before", "after"],
+        "definition": ("Should operator be placed before or after newlines."),
+    },
 }
 
 
diff --git a/src/sqlfluff/rules/L007.py b/src/sqlfluff/rules/L007.py
index 623cae8fd..4315c6483 100644
--- a/src/sqlfluff/rules/L007.py
+++ b/src/sqlfluff/rules/L007.py
@@ -19,7 +19,7 @@ class Rule_L007(BaseRule):
 
 
     | **Best practice**
-    | Place the operator after the newline.
+    | Place the operator after the newline. Unless specified by ``operator_new_lines = after``
 
     .. code-block:: sql
 
@@ -29,6 +29,8 @@ class Rule_L007(BaseRule):
         FROM foo
     """
 
+    config_keywords = ["operator_new_lines"]
+
     def _eval(self, segment, memory, parent_stack, **kwargs):
         """Operators near newlines should be after, not before the newline.
 
@@ -47,17 +49,26 @@ class Rule_L007(BaseRule):
             if segment.is_code:
                 # This is code, what kind?
                 if segment.is_type("binary_operator", "comparison_operator"):
-                    # We only trigger if the last was an operator, not if this is.
-                    pass
+                    # If it's an operator, then check if in "before" mode
+                    if self.operator_new_lines == "before":
+                        # If we're in "before" mode, then check if newline since last code
+                        for s in memory["since_code"]:
+                            if s.name == "newline":
+                                # Had a newline - so mark this operator as a fail
+                                anchor = segment
+                                # TODO: Work out a nice fix for this failure.
                 elif memory["last_code"] and memory["last_code"].is_type(
                     "binary_operator", "comparison_operator"
                 ):
-                    # It's not an operator, but the last code was. Now check to see
-                    # there is a newline between us and the last operator.
-                    for s in memory["since_code"]:
-                        if s.name == "newline":
-                            anchor = memory["last_code"]
-                            # TODO: Work out a nice fix for this.
+                    # It's not an operator, but the last code was.
+                    if self.operator_new_lines != "before":
+                        # If in "after" mode, then check to see
+                        # there is a newline between us and the last operator.
+                        for s in memory["since_code"]:
+                            if s.name == "newline":
+                                # Had a newline - so mark last operator as a fail
+                                anchor = memory["last_code"]
+                                # TODO: Work out a nice fix for this failure.
                 # Prepare memory for later
                 memory["last_code"] = segment
                 memory["since_code"] = []


Test patch:
diff --git a/test/fixtures/rules/std_rule_cases/L007.yml b/test/fixtures/rules/std_rule_cases/L007.yml
new file mode 100644
index 000000000..026e204b5
--- /dev/null
+++ b/test/fixtures/rules/std_rule_cases/L007.yml
@@ -0,0 +1,57 @@
+rule: L007
+
+passes_on_before_default:
+  pass_str: |
+    select
+        a
+        + b
+    from foo
+
+fails_on_after_default:
+  fail_str: |
+    select
+        a +
+        b
+    from foo
+
+passes_on_before_explicit:
+  pass_str: |
+    select
+        a
+        + b
+    from foo
+
+  configs: &operator_after
+    rules:
+      L007:
+        operator_new_lines: after
+
+fails_on_after_explicit:
+  fail_str: |
+    select
+        a +
+        b
+    from foo
+
+  configs: *operator_after
+
+fails_on_before_override:
+  fail_str: |
+    select
+        a
+        + b
+    from foo
+
+  configs: &operator_before
+    rules:
+      L007:
+        operator_new_lines: before
+
+passes_on_after_override:
+  pass_str: |
+    select
+        a +
+        b
+    from foo
+
+  configs: *operator_before