Repository: sqlfluff/sqlfluff

Issue:
Converting trailing commas to leading causes unexpected indentation in specific cases
### Search before asking

- [X] I searched the [issues](https://github.com/sqlfluff/sqlfluff/issues) and found no similar issues.


### What Happened

In some cases where SQLFluff is fixing/linting trailing commas to be leading commas within a SELECT statement, the following elements are indented forward for reasons unknown. 

Specifically, this seems to occur when a trailing comma immediately follows an aliased element, and then that element is followed by an empty blank line. For example:

```
SELECT
    acct_id,
    date_x,
    't' AS test,

    CASE
        WHEN condition_1 = '1' THEN ''
        ELSE condition_1
    END AS case_1,

    CASE
        WHEN condition_2 = '2' THEN ''
        ELSE condition_2
    END AS case_2,

    CASE
        WHEN condition_3 = '3' THEN ''
        ELSE condition_3
    END AS case_3,

    closing_date as cls_dt,
    CASE
        WHEN condition_4 = '4' THEN ''
        ELSE condition_4
    END AS case_4,
    dollar_amt,
FROM
    table_x
```

In this example, `'t' AS test,` is followed by an empty line, and then a `CASE` statement. 

### Expected Behaviour

I would expect the configured package to transform the above query into:

```
SELECT
    acct_id
    , date_x
    , 't' AS test

    , CASE
        WHEN condition_1 = '1' THEN ''
        ELSE condition_1
    END AS case_1

    , CASE
        WHEN condition_2 = '2' THEN ''
        ELSE condition_2
    END AS case_2

    , CASE
        WHEN condition_3 = '3' THEN ''
        ELSE condition_3
    END AS case_3

    , closing_date AS cls_dt
    , CASE
        WHEN condition_4 = '4' THEN ''
        ELSE condition_4
    END AS case_4
    , dollar_amt
FROM
    table_x

```

Nothing has changed, except for the commas becoming leading instead of trailing. 

### Observed Behaviour

Instead, this happens:

```
SELECT
    acct_id
    , date_x
    , 't'
        AS test

        , CASE
            WHEN condition_1 = '1' THEN ''
            ELSE condition_1
        END
            AS case_1

            , CASE
                WHEN condition_2 = '2' THEN ''
                ELSE condition_2
            END
                AS case_2

                , CASE
                    WHEN condition_3 = '3' THEN ''
                    ELSE condition_3
                END
                    AS case_3

                    , closing_date AS cls_dt
                    , CASE
                        WHEN condition_4 = '4' THEN ''
                        ELSE condition_4
                    END AS case_4
                    , dollar_amt
                FROM
                    table_x
```

The `AS` statements get forced onto a new line, and each consecutive `CASE` statement is indented forward when following an empty line. 

### How to reproduce

Create a file named `test.sql` with the original query:

```
SELECT
    acct_id,
    date_x,
    't' AS test,

    CASE
        WHEN condition_1 = '1' THEN ''
        ELSE condition_1
    END AS case_1,

    CASE
        WHEN condition_2 = '2' THEN ''
        ELSE condition_2
    END AS case_2,

    CASE
        WHEN condition_3 = '3' THEN ''
        ELSE condition_3
    END AS case_3,

    closing_date as cls_dt,
    CASE
        WHEN condition_4 = '4' THEN ''
        ELSE condition_4
    END AS case_4,
    dollar_amt,
FROM
    table_x
```

Create a `.sqlfluff` config file in the same folder as follows:

```
[sqlfluff]
dialect = snowflake
templater = jinja
exclude_rules = LT05, ST06
ignore = templating
ignore_templated_areas = True
sql_file_exts = .sql,.sql.j2,.dml,.ddl
processes = 0
max_line_length = 120
large_file_skip_byte_limit = 0

# Commas should be leading and touch the word that they're following
[sqlfluff:layout:type:comma]
spacing_before = touch
line_position = leading
```

And then run `sqlfluff fix test.sql --dialect snowflake`

### Dialect

Snowflake

### Version

sqlfluff==2.3.5
python==3.11.4

### Configuration

```
[sqlfluff]
# See template here: https://docs.sqlfluff.com/en/stable/configuration.html#default-configuration
dialect = snowflake
templater = jinja
# Exclude rule ST06 - column_order
exclude_rules = LT05, ST06
# exclude_rules = structure.column_order
# Avoids issues with qrun jinja templating
ignore = templating
ignore_templated_areas = True
sql_file_exts = .sql,.sql.j2,.dml,.ddl
processes = 0
max_line_length = 120
large_file_skip_byte_limit = 0
 
# Statements must end with a semicolon on a new line; the new line
# helps us insert new filters, etc. without needing mouse precision
[sqlfluff:rules:convention.terminator]
multiline_newline = True
require_final_semicolon = False
 
# Keywords should be capitalized
[sqlfluff:rules:capitalisation.keywords]
capitalisation_policy = upper

# Functions should be capitalized
[sqlfluff:rules:capitalisation.functions]
extended_capitalisation_policy = upper

[sqlfluff:rules:capitalisation.literals]
capitalisation_policy = upper

# Data types should be lowercase
[sqlfluff:rules:capitalisation.types]
extended_capitalisation_policy = upper

# Column identifiers should be in lowercase
[sqlfluff:rules:capitalisation.identifiers]
extended_capitalisation_policy = lower
 
# You must always specify the kind of join you're using (left, inner, etc.)
[sqlfluff:rules:ambiguous.join]
# Fully qualify INNER JOIN clause
fully_qualify_join_types = inner

# Group by statements must be explicit, not by position;
# if this is upsetting, consider <consistent> which
# will allow both numbering (implicit) and labeling (explicit)
[sqlfluff:rules:ambiguous.column_references]
# GROUP BY/ORDER BY column references
group_by_and_order_by_style = explicit

# Aliases must be exclicit; i.e. always use the AS clause
[sqlfluff:rules:aliasing.table]
aliasing = explicit
[sqlfluff:rules:aliasing.column]
aliasing = explicit

# Aliases must be 3 characters or more
[sqlfluff:rules:aliasing.length]
min_alias_length = 3
 
# Subqueries not allowed; prefer CTEs
# Temporarily excluded; see https://github.com/sqlfluff/sqlfluff/issues/4782
# [sqlfluff:rules:structure:subquery]
# forbid_subquery_in = both

# Ignore long comments; handle these manually
[sqlfluff:rules:layout.long_lines]
ignore_comment_lines = True
ignore_comment_clauses = True
 
# Optimize tab spacing to fit contents with 80 characters where possible
# Contents of all statements should be indented; CTEs should be indented
[sqlfluff:indentation]
tab_space_size = 4
trailing_comments = before
indented_on_contents = True
indented_using_on = False

# Commas should be leading and touch the word that they're following
[sqlfluff:layout:type:comma]
spacing_before = touch
line_position = leading

[sqlfluff:layout:type:comment]
spacing_before = any
spacing_after = any

# By setting a selection of clauses to "alone", we hint to the reflow
# algorithm that in the case of a long single line statement, the
# first place to add newlines would be around these clauses.
# Setting this to "alone:strict" would always _force_ line breaks
# around them even if the line isn't too long.
[sqlfluff:layout:type:set_operator]
line_position = alone:strict

[sqlfluff:layout:type:select_clause]
line_position = alone:strict

[sqlfluff:layout:type:from_clause]
line_position = alone:strict

[sqlfluff:layout:type:where_clause]
line_position = alone:strict

[sqlfluff:layout:type:join_clause]
line_position = alone:strict

[sqlfluff:layout:type:groupby_clause]
line_position = alone:strict

[sqlfluff:layout:type:orderby_clause]
# NOTE: Order by clauses appear in many places other than in a select
# clause. To avoid unexpected behaviour we use `leading` in this
# case rather than `alone`.
line_position = leading

[sqlfluff:layout:type:having_clause]
line_position = alone:strict

[sqlfluff:layout:type:limit_clause]
line_position = alone:strict

```

### Are you willing to work on and submit a PR to address the issue?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/sqlfluff/sqlfluff/blob/main/CODE_OF_CONDUCT.md)


Discussion:
github-actions[bot]: # Coverage Results ✅
```
Name    Stmts   Miss  Cover   Missing
-------------------------------------
TOTAL   17986      0   100%

228 files skipped due to complete coverage.

```

coveralls: 
[![Coverage Status](https://coveralls.io/builds/69212305/badge)](https://coveralls.io/builds/69212305)

coverage: 99.985%. remained the same
when pulling **76d155f602863924a29b319d13a8ac276f5ddad4 on keraion:LT02_LT04_dedent_interaction**
into **473b6f1a08aeb84d43e8ec33245ecd7f7e9149ee on sqlfluff:main**.


Patch:
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index 6f1a3dbea..b480afd96 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -1,7 +1,9 @@
 - id: sqlfluff-lint
   name: sqlfluff-lint
   # Set `--processes 0` to use maximum parallelism
-  entry: sqlfluff lint --processes 0
+  #  - `--disable-progress-bar` pre-commit suppresses logging already
+  #      this can cause an unneeded slow down.
+  entry: sqlfluff lint --processes 0 --disable-progress-bar
   language: python
   description: "Lints sql files with `SQLFluff`"
   types: [sql]
@@ -13,8 +15,10 @@
   # Set a couple of default flags:
   #  - `--show-lint-violations` shows issues to not require running `sqlfluff lint`
   #  - `--processes 0` to use maximum parallelism
+  #  - `--disable-progress-bar` pre-commit suppresses logging already
+  #      this can cause an unneeded slow down.
   # By default, this hook applies all rules.
-  entry: sqlfluff fix --show-lint-violations --processes 0
+  entry: sqlfluff fix --show-lint-violations --processes 0 --disable-progress-bar
   language: python
   description: "Fixes sql lint errors with `SQLFluff`"
   types: [sql]
diff --git a/src/sqlfluff/utils/reflow/rebreak.py b/src/sqlfluff/utils/reflow/rebreak.py
index d27625ee7..0e525ca5c 100644
--- a/src/sqlfluff/utils/reflow/rebreak.py
+++ b/src/sqlfluff/utils/reflow/rebreak.py
@@ -381,7 +381,8 @@ def rebreak_sequence(
                 # the _preceding_ point.
                 fixes.append(LintFix.delete(loc.target))
                 for seg in elem_buff[loc.prev.adj_pt_idx].segments:
-                    fixes.append(LintFix.delete(seg))
+                    if not seg.is_type("dedent"):
+                        fixes.append(LintFix.delete(seg))
 
                 # We always reinsert after the first point, but respace
                 # the inserted point to ensure it's the right size given


Test patch:
diff --git a/test/rules/std_LT02_LT04_test.py b/test/rules/std_LT02_LT04_test.py
new file mode 100644
index 000000000..65a2675f2
--- /dev/null
+++ b/test/rules/std_LT02_LT04_test.py
@@ -0,0 +1,74 @@
+"""Tests the python routines within LT02 and LT04."""
+
+import pytest
+
+from sqlfluff.core import FluffConfig, Linter
+
+
+@pytest.mark.parametrize(
+    ["in_sql", "out_sql"],
+    [
+        (
+            """SELECT
+    acct_id,
+    date_x,
+    't' AS test,
+
+    CASE
+        WHEN condition_1 = '1' THEN ''
+        ELSE condition_1
+    END AS case_1,
+
+    CASE
+        WHEN condition_2 = '2' THEN ''
+        ELSE condition_2
+    END AS case_2,
+    dollar_amt,
+FROM
+    table_x""",
+            """SELECT
+    acct_id
+    , date_x
+    , 't' AS test
+
+    , CASE
+        WHEN condition_1 = '1' THEN ''
+        ELSE condition_1
+    END AS case_1
+
+    , CASE
+        WHEN condition_2 = '2' THEN ''
+        ELSE condition_2
+    END AS case_2
+    , dollar_amt,
+FROM
+    table_x""",
+        ),
+    ],
+)
+def test_rules_std_LT02_LT04_interaction_indentation_leading(in_sql, out_sql) -> None:
+    """Test interaction between LT02 and LT04.
+
+    Test sql with two newlines with trailing commas expecting leading.
+    """
+    # Lint expected rules.
+    cfg = FluffConfig.from_string(
+        """[sqlfluff]
+dialect = snowflake
+rules = LT02, LT04
+
+[sqlfluff:layout:type:comma]
+spacing_before = touch
+line_position = leading
+"""
+    )
+    linter = Linter(config=cfg)
+
+    # Return linted/fixed file.
+    linted_file = linter.lint_string(in_sql, fix=True)
+
+    # Check expected lint errors are raised.
+    assert set([v.rule.code for v in linted_file.violations]) == {"LT04"}
+
+    # Check file is fixed.
+    assert linted_file.fix_string()[0] == out_sql