Repository: jackdewinter/pymarkdown

Issue:
Md031 fix: Some double drop cases are not producing the correct Markdown.
## Prerequisites

* [X] Are you running the latest version of this application?
* [X] Have you checked the [Frequently Asked Questions](https://pymarkdown.readthedocs.io/en/latest/faq) document?
* [X] Have you simplified the bug report to the essential details?
  * [X] Do you have a distinct command line to report?
  * [X] Can you clearly state the configuration for this bug report?
  * [X] Do you have a minimal document that highlights this bug?
  * [X] Are any required files (configuration or Markdown document) attached to the issue?
* [X] Did you perform a cursory search of other issues to look for related issues?

## Bug Report

### Bug Type

* [X] Fix/Rule not working as expected

### Description

Cases in which two containers are dropped on a single line are not producing the correct fixes for MD031.

### Specifics

What operating system and version are you running into this behavior on?

Win10

What version are you seeing this behavior in? (Run `pip list` or `pipenv run pip list` and look for the entry beside `pymarkdownlnt`.)

latest, amin

Are there any extra steps that need to be taken before executing the application?

No

What is the command line you invoke to get this behavior?

bad_fenced_block_in_list_in_block_quote_in_list_with_previous_block_double_drop
bad_fenced_block_in_block_quote_in_block_quote_with_previous_inner_list_double_drop
bad_fenced_block_in_block_quote_in_block_quote_in_block_quote_with_previous_list_double_drop
bad_fenced_block_in_block_quote_in_block_quote_in_list_with_previous_inner_list_double_drop
bad_fenced_block_in_block_quote_in_block_quote_in_list_with_previous_list_double_drop

Are you using a configuration file? Either on the command line or one of the implicit configuration files? If so, attach that file to this issue.

No

What Markdown document causes this behavior to manifest?  Attach that file to this issue.

See test cases above.

### Actual Behavior

In each of the above cases, the dropping of two containers on one line produced Markdown that was not correct.

### Expected Behavior

The fix should produce the correct Markdown when rehydrated.

Discussion:
sourcery-ai[bot]: <!-- Generated by sourcery-ai[bot]: start review_guide -->

🧙 Sourcery is reviewing your pull request!

---

<details>
<summary>Tips and commands</summary>

#### Interacting with Sourcery

- **Trigger a new review:** Comment `@sourcery-ai review` on the pull request.
- **Continue discussions:** Reply directly to Sourcery's review comments.
- **Generate a GitHub issue from a review comment:** Ask Sourcery to create an
  issue from a review comment by replying to it.
- **Generate a pull request title:** Write `@sourcery-ai` anywhere in the pull
  request title to generate a title at any time.
- **Generate a pull request summary:** Write `@sourcery-ai summary` anywhere in
  the pull request body to generate a PR summary at any time. You can also use
  this command to specify where the summary should be inserted.

#### Customizing Your Experience

Access your [dashboard](https://app.sourcery.ai) to:
- Enable or disable review features such as the Sourcery-generated pull request
  summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.

#### Getting Help

- [Contact our support team](mailto:support@sourcery.ai) for questions or feedback.
- Visit our [documentation](https://docs.sourcery.ai) for detailed guides and information.
- Keep in touch with the Sourcery team by following us on [X/Twitter](https://x.com/SourceryAI), [LinkedIn](https://www.linkedin.com/company/sourcery-ai/) or [GitHub](https://github.com/sourcery-ai).

</details>

<!-- Generated by sourcery-ai[bot]: end review_guide -->
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/jackdewinter/pymarkdown/pull/1251?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
Attention: Patch coverage is `98.91304%` with `1 line` in your changes missing coverage. Please review.
> Project coverage is 99.98%. Comparing base [(`50a1e94`)](https://app.codecov.io/gh/jackdewinter/pymarkdown/commit/50a1e9487d32589015ec920edc987d5d3ba911f8?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) to head [(`ecef8f0`)](https://app.codecov.io/gh/jackdewinter/pymarkdown/commit/ecef8f03b7bd075ecfff6c829d7e0faef570ccf5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).

| [Files with missing lines](https://app.codecov.io/gh/jackdewinter/pymarkdown/pull/1251?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Patch % | Lines |
|---|---|---|
| [pymarkdown/plugins/rule\_md\_031.py](https://app.codecov.io/gh/jackdewinter/pymarkdown/pull/1251?src=pr&el=tree&filepath=pymarkdown%2Fplugins%2Frule_md_031.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cHltYXJrZG93bi9wbHVnaW5zL3J1bGVfbWRfMDMxLnB5) | 98.91% | [0 Missing and 1 partial :warning: ](https://app.codecov.io/gh/jackdewinter/pymarkdown/pull/1251?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1251      +/-   ##
==========================================
- Coverage   99.98%   99.98%   -0.01%     
==========================================
  Files         191      191              
  Lines       21123    21194      +71     
  Branches     2701     2715      +14     
==========================================
+ Hits        21120    21190      +70     
  Misses          1        1              
- Partials        2        3       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/jackdewinter/pymarkdown/pull/1251?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).


Patch:
diff --git a/newdocs/src/changelog.md b/newdocs/src/changelog.md
index 9acc115f..a2a05616 100644
--- a/newdocs/src/changelog.md
+++ b/newdocs/src/changelog.md
@@ -27,6 +27,9 @@
     - In 3+ drop cases with only lists and no block quotes, indent is
       not calculated properly on rehydrate.  This in turn causes the
       fixed text to be wrong.
+- [Issue 1250](https://github.com/jackdewinter/pymarkdown/issues/1250)
+    - Batch of fixes for cases when Md031 is trying to properly space
+      a fenced code block after dropping 2 containers.
 
 <!--- pyml disable-next-line no-duplicate-heading-->
 ### Changed
diff --git a/publish/coverage.json b/publish/coverage.json
index 11801c21..ef9a15f3 100644
--- a/publish/coverage.json
+++ b/publish/coverage.json
@@ -2,12 +2,12 @@
     "projectName": "pymarkdown",
     "reportSource": "pytest",
     "branchLevel": {
-        "totalMeasured": 5403,
-        "totalCovered": 5401
+        "totalMeasured": 5431,
+        "totalCovered": 5428
     },
     "lineLevel": {
-        "totalMeasured": 21123,
-        "totalCovered": 21122
+        "totalMeasured": 21194,
+        "totalCovered": 21193
     }
 }
 
diff --git a/pymarkdown/plugins/rule_md_031.py b/pymarkdown/plugins/rule_md_031.py
index c6ebf6fc..715cf2ef 100644
--- a/pymarkdown/plugins/rule_md_031.py
+++ b/pymarkdown/plugins/rule_md_031.py
@@ -35,6 +35,7 @@ class PendingContainerAdjustment:
     insert_index: int
     leading_space_to_insert: str
     do_insert: bool = True
+    do_special: bool = False
 
 
 # pylint: disable=too-many-instance-attributes
@@ -53,6 +54,9 @@ class RuleMd031(RulePlugin):
         self.__container_adjustments: List[List[PendingContainerAdjustment]] = []
         self.__fix_count = 0
         self.__removed_container_stack_token: Optional[MarkdownToken] = None
+        self.__x1: List[MarkdownToken] = []
+        self.__x2: List[List[PendingContainerAdjustment]] = []
+        self.__x3: List[int] = []
         self.__removed_container_adjustments: Optional[
             List[PendingContainerAdjustment]
         ] = None
@@ -103,6 +107,9 @@ class RuleMd031(RulePlugin):
         self.__leading_space_index_tracker.clear()
         self.__removed_container_adjustments = None
         self.__removed_container_stack_token = None
+        self.__x1.clear()
+        self.__x2.clear()
+        self.__x3.clear()
 
     def __fix_spacing_special_case(
         self, context: PluginScanContext, token: MarkdownToken
@@ -123,6 +130,7 @@ class RuleMd031(RulePlugin):
         self.register_replace_tokens_request(
             context, self.__last_token, token, replacement_tokens
         )
+
         end_token = cast(EndMarkdownToken, self.__last_token)
         block_quote_start_token = cast(
             BlockQuoteMarkdownToken, end_token.start_markdown_token
@@ -137,7 +145,9 @@ class RuleMd031(RulePlugin):
             )
         )
 
-    def __fix_spacing_block_quote(self, token: MarkdownToken) -> None:
+    def __fix_spacing_block_quote(
+        self, token: MarkdownToken, upgrade_kludge: bool
+    ) -> None:
         leading_space_insert_index = self.__leading_space_index_tracker.get_tokens_block_quote_bleading_space_index(
             token
         )
@@ -153,12 +163,23 @@ class RuleMd031(RulePlugin):
         )
         assert block_quote_token.bleading_spaces is not None
         split_leading_space = block_quote_token.bleading_spaces.split("\n")
-        former_item_leading_space = split_leading_space[
-            leading_space_insert_index
-        ].rstrip()
+
+        former_item_leading_space = split_leading_space[leading_space_insert_index]
+        if (
+            leading_space_insert_index == len(split_leading_space) - 1
+            and not former_item_leading_space
+            and leading_space_insert_index
+        ):
+            former_item_leading_space = split_leading_space[
+                leading_space_insert_index - 1
+            ]
+        if not upgrade_kludge:
+            former_item_leading_space = former_item_leading_space.rstrip()
         self.__container_adjustments[container_index].append(
             PendingContainerAdjustment(
-                leading_space_insert_index, former_item_leading_space
+                leading_space_insert_index,
+                former_item_leading_space,
+                do_special=upgrade_kludge,
             )
         )
 
@@ -464,13 +485,13 @@ class RuleMd031(RulePlugin):
         )
 
     def __fix_spacing_one_container(
-        self, context: PluginScanContext, token: MarkdownToken
+        self, context: PluginScanContext, token: MarkdownToken, upgrade_kludge: bool
     ) -> bool:
         did_special_list_fix = False
         if self.__leading_space_index_tracker.get_container_stack_item(
             -1
         ).is_block_quote_start:
-            self.__fix_spacing_block_quote(token)
+            self.__fix_spacing_block_quote(token, upgrade_kludge)
         else:
             did_special_list_fix = self.__fix_spacing_list(context, token)
         return did_special_list_fix
@@ -497,15 +518,179 @@ class RuleMd031(RulePlugin):
                 "\n".join(split_spaces),
             )
 
+    def __fix_spacing_with_fenced_and_list_end(
+        self, context: PluginScanContext, token: MarkdownToken, new_token: MarkdownToken
+    ) -> None:
+        end_container_index = len(self.__last_end_container_tokens) - 1
+        while (
+            end_container_index >= 0
+            and self.__last_end_container_tokens[end_container_index].is_list_end
+        ):
+            end_container_index -= 1
+        first_token = self.__last_end_container_tokens[end_container_index + 1]
+        replacement_tokens: List[MarkdownToken] = [
+            BlankLineMarkdownToken(
+                extracted_whitespace="",
+                position_marker=PositionMarker(new_token.line_number - 1, 0, ""),
+                column_delta=1,
+            )
+        ]
+        replacement_tokens.extend(
+            self.__last_end_container_tokens[end_container_index + 1 :]
+        )
+        replacement_tokens.append(new_token)
+        self.register_replace_tokens_request(
+            context, first_token, token, replacement_tokens
+        )
+
+    def __fix_spacing_with_special_list_fix(
+        self, context: PluginScanContext, token: MarkdownToken, new_token: MarkdownToken
+    ) -> None:
+        assert self.__last_token and self.__last_token.is_block_quote_end
+        new_end_token = cast(EndMarkdownToken, copy.copy(self.__last_token))
+        new_end_token.set_extra_end_data(None)
+        replacement_tokens = [
+            BlankLineMarkdownToken(
+                extracted_whitespace="",
+                position_marker=PositionMarker(new_token.line_number - 1, 0, ""),
+                column_delta=1,
+            ),
+            new_end_token,
+            new_token,
+        ]
+        self.register_replace_tokens_request(
+            context, self.__last_token, token, replacement_tokens
+        )
+
+    def __fix_spacing_with_else(
+        self, context: PluginScanContext, token: MarkdownToken, new_token: MarkdownToken
+    ) -> None:
+        replacement_tokens = [
+            BlankLineMarkdownToken(
+                extracted_whitespace="",
+                position_marker=PositionMarker(new_token.line_number - 1, 0, ""),
+                column_delta=1,
+            ),
+            new_token,
+        ]
+        self.register_replace_tokens_request(context, token, token, replacement_tokens)
+
+    def __calc_kludge_one(self, at_least_one_container: bool) -> bool:
+        is_kludge_one = False
+        if at_least_one_container:
+            last_stack_token = (
+                self.__leading_space_index_tracker.get_container_stack_item(-1)
+            )
+            if last_stack_token.is_list_start and self.__x1[-1].is_block_quote_start:
+                is_kludge_one = not any(i.is_block_quote_start for i in self.__x1[:-1])
+        return is_kludge_one
+
+    def __calc_2(self, context: PluginScanContext, did_process_removals: bool) -> bool:
+
+        # This will most likely need rewriting for deeper nestings.
+        if (
+            not did_process_removals
+            and len(self.__x1) == 2
+            and self.__x1[0].is_block_quote_start
+            and self.__x1[1].is_list_start
+        ):
+            did_process_removals = self.__apply_tailing_block_quote_fix(0, context)
+        return did_process_removals
+
+    def __calc_3(
+        self,
+        context: PluginScanContext,
+        did_process_removals: bool,
+        at_least_one_container: bool,
+        upgrade_kludge: bool,
+    ) -> Tuple[bool, bool]:
+        # This will most likely need rewriting for deeper nestings.
+        if not did_process_removals and at_least_one_container:
+            last_stack_token_index = (
+                self.__leading_space_index_tracker.get_container_stack_size() - 1
+            )
+            found_block_quote_token = None
+            assert last_stack_token_index >= 0
+            assert (
+                token_at_index := self.__leading_space_index_tracker.get_container_stack_item(
+                    last_stack_token_index
+                )
+            ).is_block_quote_start
+            found_block_quote_token = token_at_index
+            # while last_stack_token_index >= 0:
+            #     if (
+            #         token_at_index := self.__leading_space_index_tracker.get_container_stack_item(
+            #             last_stack_token_index
+            #         )
+            #     ).is_block_quote_start:
+            #         found_block_quote_token = token_at_index
+            #         break
+            #     last_stack_token_index -= 1
+            if (
+                found_block_quote_token
+                and len(self.__x1) == 2
+                and self.__x1[0].is_list_start
+                and self.__x1[1].is_block_quote_start
+            ):
+                did_process_removals = upgrade_kludge = (
+                    self.__apply_tailing_block_quote_fix(1, context)
+                )
+        return did_process_removals, upgrade_kludge
+
+    def __apply_tailing_block_quote_fix(
+        self, modify_index: int, context: PluginScanContext
+    ) -> bool:
+
+        assert self.__x1[modify_index].is_block_quote_start
+        block_quote_token = cast(BlockQuoteMarkdownToken, self.__x1[modify_index])
+        assert block_quote_token.bleading_spaces is not None
+        split_spaces = block_quote_token.bleading_spaces.split("\n")
+        assert self.__x3[modify_index] == len(split_spaces) - 1
+        split_spaces[-1] = split_spaces[-1].rstrip()
+        self.register_fix_token_request(
+            context,
+            self.__x1[modify_index],
+            "next_token",
+            "bleading_spaces",
+            "\n".join(split_spaces),
+        )
+        return True
+
     def __fix_spacing(
         self, context: PluginScanContext, token: MarkdownToken, special_case: bool
     ) -> None:
-        did_special_list_fix = False
         if special_case:
             self.__fix_spacing_special_case(context, token)
             return
-        if self.__leading_space_index_tracker.in_at_least_one_container():
-            did_special_list_fix = self.__fix_spacing_one_container(context, token)
+        at_least_one_container = (
+            self.__leading_space_index_tracker.in_at_least_one_container()
+        )
+        upgrade_kludge = False
+        if len(self.__x1) > 1:
+            # These circumstances are already handled by the zero and single level removals.
+            all_removals_are_block_quotes = all(
+                f.is_block_quote_start for f in self.__x1
+            )
+            all_removals_are_lists = all(f.is_list_start for f in self.__x1)
+            was_kludge_one = self.__calc_kludge_one(at_least_one_container)
+            did_process_removals = (
+                all_removals_are_block_quotes
+                or all_removals_are_lists
+                or was_kludge_one
+                or not at_least_one_container
+            )
+
+            did_process_removals = self.__calc_2(context, did_process_removals)
+            did_process_removals, upgrade_kludge = self.__calc_3(
+                context, did_process_removals, at_least_one_container, upgrade_kludge
+            )
+            # assert did_process_removals
+
+        did_special_list_fix = False
+        if at_least_one_container:
+            did_special_list_fix = self.__fix_spacing_one_container(
+                context, token, upgrade_kludge
+            )
         elif self.__removed_container_stack_token:
             self.__fix_spacing_removed_container(context, token)
 
@@ -514,55 +699,11 @@ class RuleMd031(RulePlugin):
         new_token.adjust_line_number(context, self.__fix_count)
         assert self.__last_token is not None
         if token.is_fenced_code_block and self.__last_token.is_list_end:
-            end_container_index = len(self.__last_end_container_tokens) - 1
-            while (
-                end_container_index >= 0
-                and self.__last_end_container_tokens[end_container_index].is_list_end
-            ):
-                end_container_index -= 1
-            first_token = self.__last_end_container_tokens[end_container_index + 1]
-            replacement_tokens: List[MarkdownToken] = [
-                BlankLineMarkdownToken(
-                    extracted_whitespace="",
-                    position_marker=PositionMarker(new_token.line_number - 1, 0, ""),
-                    column_delta=1,
-                )
-            ]
-            replacement_tokens.extend(
-                self.__last_end_container_tokens[end_container_index + 1 :]
-            )
-            replacement_tokens.append(new_token)
-            self.register_replace_tokens_request(
-                context, first_token, token, replacement_tokens
-            )
+            self.__fix_spacing_with_fenced_and_list_end(context, token, new_token)
         elif did_special_list_fix:
-            assert self.__last_token and self.__last_token.is_block_quote_end
-            new_end_token = cast(EndMarkdownToken, copy.copy(self.__last_token))
-            new_end_token.set_extra_end_data(None)
-            replacement_tokens = [
-                BlankLineMarkdownToken(
-                    extracted_whitespace="",
-                    position_marker=PositionMarker(new_token.line_number - 1, 0, ""),
-                    column_delta=1,
-                ),
-                new_end_token,
-                new_token,
-            ]
-            self.register_replace_tokens_request(
-                context, self.__last_token, token, replacement_tokens
-            )
+            self.__fix_spacing_with_special_list_fix(context, token, new_token)
         else:
-            replacement_tokens = [
-                BlankLineMarkdownToken(
-                    extracted_whitespace="",
-                    position_marker=PositionMarker(new_token.line_number - 1, 0, ""),
-                    column_delta=1,
-                ),
-                new_token,
-            ]
-            self.register_replace_tokens_request(
-                context, token, token, replacement_tokens
-            )
+            self.__fix_spacing_with_else(context, token, new_token)
 
     def __handle_fenced_code_block(
         self, context: PluginScanContext, token: MarkdownToken, special_case: bool
@@ -677,7 +818,16 @@ class RuleMd031(RulePlugin):
             ), "Pending containers means this should at least have a newline in it."
             split_spaces = list_token.leading_spaces.split("\n")
 
+        for i, next_container_adjustment in enumerate(next_container_adjustment_list):
+            if next_container_adjustment.do_special:
+                split_spaces.insert(
+                    0, next_container_adjustment.leading_space_to_insert
+                )
+                del next_container_adjustment_list[i]
+                break
+
         for next_container_adjustment in next_container_adjustment_list[::-1]:
+            assert not next_container_adjustment.do_special
             if next_container_adjustment.do_insert:
                 split_spaces.insert(
                     next_container_adjustment.insert_index,
@@ -706,10 +856,22 @@ class RuleMd031(RulePlugin):
                         context, next_container_adjustment_list
                     )
 
+            if self.__leading_space_index_tracker.get_container_stack_item(
+                -1
+            ).is_block_quote_start:
+                leading_space_insert_index = self.__leading_space_index_tracker.get_tokens_block_quote_bleading_space_index(
+                    token
+                )
+            else:
+                leading_space_insert_index = -10
+            self.__x3.append(leading_space_insert_index)
+
             self.__removed_container_stack_token = (
                 self.__leading_space_index_tracker.process_container_end(token)
             )
+            self.__x1.append(self.__removed_container_stack_token)
             self.__removed_container_adjustments = self.__container_adjustments[-1]
+            self.__x2.append(self.__removed_container_adjustments)
             del self.__container_adjustments[-1]
 
     def __calculate_special_case(
@@ -773,7 +935,10 @@ class RuleMd031(RulePlugin):
         self.__second_last_token = self.__last_token
         self.__last_token = token
         self.__removed_container_stack_token = None
+        self.__x1.clear()
         self.__removed_container_adjustments = None
+        self.__x2.clear()
+        self.__x3.clear()
 
 
 # pylint: enable=too-many-instance-attributes


Test patch:
diff --git a/publish/test-results.json b/publish/test-results.json
index 0fe7cc94..979e1298 100644
--- a/publish/test-results.json
+++ b/publish/test-results.json
@@ -1367,7 +1367,7 @@
             "totalTests": 653,
             "failedTests": 0,
             "errorTests": 0,
-            "skippedTests": 61,
+            "skippedTests": 59,
             "elapsedTimeInMilliseconds": 0
         },
         {
@@ -1620,7 +1620,7 @@
         },
         {
             "name": "test.test_markdown_extra",
-            "totalTests": 210,
+            "totalTests": 220,
             "failedTests": 0,
             "errorTests": 0,
             "skippedTests": 3,
diff --git a/test/rules/test_md031.py b/test/rules/test_md031.py
index d1538cae..79045e92 100644
--- a/test/rules/test_md031.py
+++ b/test/rules/test_md031.py
@@ -2174,7 +2174,7 @@ A code block
 > > ```
 """,
     ),
-    pluginRuleTest(
+    pluginRuleTest(  # test_extra_050g0 test_extra_050g1
         "bad_fenced_block_in_block_quote_in_block_quote_double_drop_with_previous_inner_block",
         source_file_contents="""> > > block 3
 > > > block 3
@@ -2189,6 +2189,7 @@ A code block
 {temp_source_path}:6:3: MD031: Fenced code blocks should be surrounded by blank lines (blanks-around-fences)
 """,
         disable_rules="md032",
+        use_fix_debug=True,
         mark_fix_as_skipped=skip_fix_bad_markdown,
         fix_expected_file_contents="""> > > block 3
 > > > block 3
@@ -2455,7 +2456,7 @@ A code block
 > > --------
 """,
     ),
-    pluginRuleTest(  # test_extra_049l0
+    pluginRuleTest(  # test_extra_049l0 test_extra_050h0 test_extra_050h1
         "bad_fenced_block_in_block_quote_in_block_quote_with_previous_inner_list_double_drop",
         source_file_contents="""> > + block 3
 > >   block 3
@@ -2470,7 +2471,8 @@ A code block
 {temp_source_path}:6:3: MD031: Fenced code blocks should be surrounded by blank lines (blanks-around-fences)
 """,
         disable_rules="md032",
-        mark_fix_as_skipped=skip_fix_bad_markdown,
+        # mark_fix_as_skipped=skip_fix_bad_markdown,
+        # use_fix_debug=True,
         fix_expected_file_contents="""> > + block 3
 > >   block 3
 > > + block 3
@@ -4465,7 +4467,7 @@ A code block
    >   ----
 """,
     ),
-    pluginRuleTest(
+    pluginRuleTest(  # test_extra_050e0 test_extra_050e1
         "bad_fenced_block_in_list_in_block_quote_in_list_with_previous_block_double_drop",
         source_file_contents="""1. > + ----
    >   > block 1
@@ -4480,8 +4482,6 @@ A code block
 {temp_source_path}:6:6: MD031: Fenced code blocks should be surrounded by blank lines (blanks-around-fences)
 """,
         disable_rules="md032,md027",
-        use_debug=True,
-        mark_fix_as_skipped=skip_fix_bad_markdown,
         fix_expected_file_contents="""1. > + ----
    >   > block 1
    >   > block 2
@@ -4689,7 +4689,7 @@ A code block
    > ----
 """,
     ),
-    pluginRuleTest(
+    pluginRuleTest(  # test_extra_050f0 test_extra_050f1
         "bad_fenced_block_in_list_in_block_quote_in_list_with_previous_list_triple_drop",
         source_file_contents="""1. > + ----
    >   + list 1
@@ -6932,7 +6932,7 @@ another list
 > + another list
 """,
     ),
-    pluginRuleTest(
+    pluginRuleTest(  # test_extra_050d0 test_extra_050d1
         "bad_fenced_block_in_list_in_list_in_block_quote_with_previous_block_double_drop",
         source_file_contents="""> + + -----
 >     > block 1
diff --git a/test/test_markdown_extra.py b/test/test_markdown_extra.py
index 191c52c5..5095eb63 100644
--- a/test/test_markdown_extra.py
+++ b/test/test_markdown_extra.py
@@ -9516,6 +9516,608 @@ block 2</p>
     act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=False)
 
 
+@pytest.mark.gfm
+def test_extra_050d0():
+    """
+    TBD
+    bad_fenced_block_in_list_in_list_in_block_quote_with_previous_block_double_drop
+    """
+
+    # Arrange
+    source_markdown = """> + + -----
+>     > block 1
+>     > block 2
+>   ```block
+>   A code block
+>   ```
+>   -----
+> + another list
+"""
+    expected_tokens = [
+        "[block-quote(1,1)::> \n> \n> \n> \n> \n> ]",
+        "[ulist(1,3):+::4::  \n  \n  \n]",
+        "[ulist(1,5):+::6:  :\n\n  þ]",
+        "[tbreak(1,7):-::-----]",
+        "[block-quote(2,7)::>     > \n>     > \n> ]",
+        "[para(2,9):\n]",
+        "[text(2,9):block 1\nblock 2::\n]",
+        "[end-para:::False]",
+        "[end-block-quote:::True]",
+        "[end-ulist:::True]",
+        "[fcode-block(4,5):`:3:block:::::]",
+        "[text(5,3):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[tbreak(7,5):-::-----]",
+        "[li(8,3):4::]",
+        "[para(8,5):]",
+        "[text(8,5):another list:]",
+        "[end-para:::True]",
+        "[BLANK(9,1):]",
+        "[end-ulist:::True]",
+        "[end-block-quote:::True]",
+    ]
+    expected_gfm = """<blockquote>
+<ul>
+<li>
+<ul>
+<li>
+<hr />
+<blockquote>
+<p>block 1
+block 2</p>
+</blockquote>
+</li>
+</ul>
+<pre><code class="language-block">A code block
+</code></pre>
+<hr />
+</li>
+<li>another list</li>
+</ul>
+</blockquote>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=False)
+
+
+@pytest.mark.gfm
+def test_extra_050d1():
+    """
+    TBD
+    bad_fenced_block_in_list_in_list_in_block_quote_with_previous_block_double_drop
+    """
+
+    # Arrange
+    source_markdown = """> + + -----
+>     > block 1
+>     > block 2
+>
+>   ```block
+>   A code block
+>   ```
+>
+>   -----
+> + another list
+"""
+    expected_tokens = [
+        "[block-quote(1,1)::> \n> \n> \n> \n> \n>\n> \n> ]",
+        "[ulist(1,3):+::4::  \n  \n\n  \n]",
+        "[ulist(1,5):+::6:  :\n\n\n  ]",
+        "[tbreak(1,7):-::-----]",
+        "[block-quote(2,7)::>     > \n>     > \n>]",
+        "[para(2,9):\n]",
+        "[text(2,9):block 1\nblock 2::\n]",
+        "[end-para:::True]",
+        "[BLANK(4,2):]",
+        "[end-block-quote:::True]",
+        "[end-ulist:::True]",
+        "[fcode-block(5,5):`:3:block:::::]",
+        "[text(6,3):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[BLANK(8,2):]",
+        "[tbreak(9,5):-::-----]",
+        "[li(10,3):4::]",
+        "[para(10,5):]",
+        "[text(10,5):another list:]",
+        "[end-para:::True]",
+        "[BLANK(11,1):]",
+        "[end-ulist:::True]",
+        "[end-block-quote:::True]",
+    ]
+    expected_gfm = """<blockquote>
+<ul>
+<li>
+<ul>
+<li>
+<hr />
+<blockquote>
+<p>block 1
+block 2</p>
+</blockquote>
+</li>
+</ul>
+<pre><code class="language-block">A code block
+</code></pre>
+<hr />
+</li>
+<li>
+<p>another list</p>
+</li>
+</ul>
+</blockquote>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=False)
+
+
+@pytest.mark.gfm
+def test_extra_050e0():
+    """
+    TBD
+    bad_fenced_block_in_list_in_block_quote_in_list_with_previous_block_double_drop
+    """
+
+    # Arrange
+    source_markdown = """1. > + ----
+   >   > block 1
+   >   > block 2
+   > ```block
+   > A code block
+   > ```
+   > ----
+"""
+    expected_tokens = [
+        "[olist(1,1):.:1:3::\n\n\n]",
+        "[block-quote(1,4):   :   > \n   > \n   > \n   > \n   > \n]",
+        "[ulist(1,6):+::7::\n\n]",
+        "[tbreak(1,8):-::----]",
+        "[block-quote(2,8)::> \n   >   > \n   > ]",
+        "[para(2,10):\n]",
+        "[text(2,10):block 1\nblock 2::\n]",
+        "[end-para:::False]",
+        "[end-block-quote:::True]",
+        "[end-ulist:::True]",
+        "[fcode-block(4,6):`:3:block:::::]",
+        "[text(5,6):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[tbreak(7,6):-::----]",
+        "[end-block-quote:::True]",
+        "[BLANK(8,1):]",
+        "[end-olist:::True]",
+    ]
+    expected_gfm = """<ol>
+<li>
+<blockquote>
+<ul>
+<li>
+<hr />
+<blockquote>
+<p>block 1
+block 2</p>
+</blockquote>
+</li>
+</ul>
+<pre><code class="language-block">A code block
+</code></pre>
+<hr />
+</blockquote>
+</li>
+</ol>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=True)
+
+
+@pytest.mark.gfm
+def test_extra_050e1():
+    """
+    TBD
+    bad_fenced_block_in_list_in_block_quote_in_list_with_previous_block_double_drop
+    """
+
+    # Arrange
+    source_markdown = """1. > + ----
+   >   > block 1
+   >   > block 2
+   >
+   > ```block
+   > A code block
+   > ```
+   >
+   > ----
+"""
+    expected_tokens = [
+        "[olist(1,1):.:1:3::\n\n\n\n]",
+        "[block-quote(1,4):   :   > \n   > \n   > \n   > \n   > \n   >\n   > \n]",
+        "[ulist(1,6):+::7::\n\n\n]",
+        "[tbreak(1,8):-::----]",
+        "[block-quote(2,8)::> \n   >   > \n   >]",
+        "[para(2,10):\n]",
+        "[text(2,10):block 1\nblock 2::\n]",
+        "[end-para:::True]",
+        "[BLANK(4,5):]",
+        "[end-block-quote:::True]",
+        "[end-ulist:::True]",
+        "[fcode-block(5,6):`:3:block:::::]",
+        "[text(6,6):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[BLANK(8,5):]",
+        "[tbreak(9,6):-::----]",
+        "[end-block-quote:::True]",
+        "[BLANK(10,1):]",
+        "[end-olist:::True]",
+    ]
+    expected_gfm = """<ol>
+<li>
+<blockquote>
+<ul>
+<li>
+<hr />
+<blockquote>
+<p>block 1
+block 2</p>
+</blockquote>
+</li>
+</ul>
+<pre><code class="language-block">A code block
+</code></pre>
+<hr />
+</blockquote>
+</li>
+</ol>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=False)
+
+
+@pytest.mark.gfm
+def test_extra_050f0():
+    """
+    TBD
+    bad_fenced_block_in_list_in_block_quote_in_list_with_previous_list_triple_drop
+    """
+
+    # Arrange
+    source_markdown = """1. > + ----
+   >   + list 1
+   >     list 2
+   >   + list 3
+   ```block
+   A code block
+   ```
+   ----
+"""
+    expected_tokens = [
+        "[olist(1,1):.:1:3::   \n   \n   \n   \n]",
+        "[block-quote(1,4):   :   > \n   > \n   > \n   > ]",
+        "[ulist(1,6):+::7:]",
+        "[tbreak(1,8):-::----]",
+        "[ulist(2,8):+::9:  :    ]",
+        "[para(2,10):\n]",
+        "[text(2,10):list 1\nlist 2::\n]",
+        "[end-para:::True]",
+        "[li(4,8):9:  :]",
+        "[para(4,10):]",
+        "[text(4,10):list 3:]",
+        "[end-para:::True]",
+        "[end-ulist:::True]",
+        "[end-ulist:::True]",
+        "[end-block-quote:::True]",
+        "[fcode-block(5,4):`:3:block:::::]",
+        "[text(6,4):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[tbreak(8,4):-::----]",
+        "[BLANK(9,1):]",
+        "[end-olist:::True]",
+    ]
+    expected_gfm = """<ol>
+<li>
+<blockquote>
+<ul>
+<li>
+<hr />
+<ul>
+<li>list 1
+list 2</li>
+<li>list 3</li>
+</ul>
+</li>
+</ul>
+</blockquote>
+<pre><code class="language-block">A code block
+</code></pre>
+<hr />
+</li>
+</ol>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=False)
+
+
+@pytest.mark.gfm
+def test_extra_050f1():
+    """
+    TBD
+    bad_fenced_block_in_list_in_block_quote_in_list_with_previous_list_triple_drop
+    """
+
+    # Arrange
+    source_markdown = """1. > + ----
+   >   + list 1
+   >     list 2
+   >   + list 3
+
+   ```block
+   A code block
+   ```
+
+   ----
+"""
+    expected_tokens = [
+        "[olist(1,1):.:1:3::   \n   \n   \n\n   \n]",
+        "[block-quote(1,4):   :   > \n   > \n   > \n   > ]",
+        "[ulist(1,6):+::7:]",
+        "[tbreak(1,8):-::----]",
+        "[ulist(2,8):+::9:  :    \n]",
+        "[para(2,10):\n]",
+        "[text(2,10):list 1\nlist 2::\n]",
+        "[end-para:::True]",
+        "[li(4,8):9:  :]",
+        "[para(4,10):]",
+        "[text(4,10):list 3:]",
+        "[end-para:::True]",
+        "[BLANK(5,1):]",
+        "[end-ulist:::True]",
+        "[end-ulist:::True]",
+        "[end-block-quote:::True]",
+        "[fcode-block(6,4):`:3:block:::::]",
+        "[text(7,4):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[BLANK(9,1):]",
+        "[tbreak(10,4):-::----]",
+        "[BLANK(11,1):]",
+        "[end-olist:::True]",
+    ]
+    expected_gfm = """<ol>
+<li>
+<blockquote>
+<ul>
+<li>
+<hr />
+<ul>
+<li>list 1
+list 2</li>
+<li>list 3</li>
+</ul>
+</li>
+</ul>
+</blockquote>
+<pre><code class="language-block">A code block
+</code></pre>
+<hr />
+</li>
+</ol>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=False)
+
+
+@pytest.mark.gfm
+def test_extra_050g0():
+    """
+    TBD
+    bad_fenced_block_in_block_quote_in_block_quote_double_drop_with_previous_inner_block
+    """
+
+    # Arrange
+    source_markdown = """> > > block 3
+> > > block 3
+> > > block 3
+> ```block
+> A code block
+> ```
+> text
+"""
+    expected_tokens = [
+        "[block-quote(1,1)::> \n> \n> \n]",
+        "[block-quote(1,3)::]",
+        "[block-quote(1,5)::> > > \n> > > \n> > > \n> ]",
+        "[para(1,7):\n\n]",
+        "[text(1,7):block 3\nblock 3\nblock 3::\n\n]",
+        "[end-para:::False]",
+        "[end-block-quote::> :True]",
+        "[end-block-quote:::True]",
+        "[fcode-block(4,3):`:3:block:::::]",
+        "[text(5,3):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[para(7,3):]",
+        "[text(7,3):text:]",
+        "[end-para:::True]",
+        "[end-block-quote:::True]",
+        "[BLANK(8,1):]",
+    ]
+    expected_gfm = """<blockquote>
+<blockquote>
+<blockquote>
+<p>block 3
+block 3
+block 3</p>
+</blockquote>
+</blockquote>
+<pre><code class="language-block">A code block
+</code></pre>
+<p>text</p>
+</blockquote>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=False)
+
+
+@pytest.mark.gfm
+def test_extra_050g1():
+    """
+    TBD
+    bad_fenced_block_in_block_quote_in_block_quote_double_drop_with_previous_inner_block
+    """
+
+    # Arrange
+    source_markdown = """> > > block 3
+> > > block 3
+> > > block 3
+>
+> ```block
+> A code block
+> ```
+>
+> text
+"""
+    expected_tokens = [
+        "[block-quote(1,1)::> \n> \n> \n>\n> \n]",
+        "[block-quote(1,3)::]",
+        "[block-quote(1,5)::> > > \n> > > \n> > > \n>]",
+        "[para(1,7):\n\n]",
+        "[text(1,7):block 3\nblock 3\nblock 3::\n\n]",
+        "[end-para:::True]",
+        "[BLANK(4,2):]",
+        "[end-block-quote:::True]",
+        "[end-block-quote:::True]",
+        "[fcode-block(5,3):`:3:block:::::]",
+        "[text(6,3):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[BLANK(8,2):]",
+        "[para(9,3):]",
+        "[text(9,3):text:]",
+        "[end-para:::True]",
+        "[end-block-quote:::True]",
+        "[BLANK(10,1):]",
+    ]
+    expected_gfm = """<blockquote>
+<blockquote>
+<blockquote>
+<p>block 3
+block 3
+block 3</p>
+</blockquote>
+</blockquote>
+<pre><code class="language-block">A code block
+</code></pre>
+<p>text</p>
+</blockquote>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=False)
+
+
+@pytest.mark.gfm
+def test_extra_050h0():
+    """
+    TBD
+    bad_fenced_block_in_block_quote_in_block_quote_with_previous_inner_list_double_drop
+    """
+
+    # Arrange
+    source_markdown = """> > + block 3
+> >   block 3
+> > + block 3
+> ```block
+> A code block
+> ```
+> --------
+"""
+    expected_tokens = [
+        "[block-quote(1,1)::> \n> \n> \n]",
+        "[block-quote(1,3)::> > \n> > \n> > \n> ]",
+        "[ulist(1,5):+::6::  \n]",
+        "[para(1,7):\n]",
+        "[text(1,7):block 3\nblock 3::\n]",
+        "[end-para:::True]",
+        "[li(3,5):6::]",
+        "[para(3,7):]",
+        "[text(3,7):block 3:]",
+        "[end-para:::False]",
+        "[end-ulist:::True]",
+        "[end-block-quote:::True]",
+        "[fcode-block(4,3):`:3:block:::::]",
+        "[text(5,3):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[tbreak(7,3):-::--------]",
+        "[end-block-quote:::True]",
+        "[BLANK(8,1):]",
+    ]
+    expected_gfm = """<blockquote>
+<blockquote>
+<ul>
+<li>block 3
+block 3</li>
+<li>block 3</li>
+</ul>
+</blockquote>
+<pre><code class="language-block">A code block
+</code></pre>
+<hr />
+</blockquote>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=False)
+
+
+@pytest.mark.gfm
+def test_extra_050h1():
+    """
+    TBD
+    bad_fenced_block_in_block_quote_in_block_quote_with_previous_inner_list_double_drop
+    """
+
+    # Arrange
+    source_markdown = """> > + block 3
+> >   block 3
+> > + block 3
+>
+> ```block
+> A code block
+> ```
+>
+> --------
+"""
+    expected_tokens = [
+        "[block-quote(1,1)::> \n> \n> \n>\n> \n]",
+        "[block-quote(1,3)::> > \n> > \n> > \n>]",
+        "[ulist(1,5):+::6::  \n]",
+        "[para(1,7):\n]",
+        "[text(1,7):block 3\nblock 3::\n]",
+        "[end-para:::True]",
+        "[li(3,5):6::]",
+        "[para(3,7):]",
+        "[text(3,7):block 3:]",
+        "[end-para:::True]",
+        "[BLANK(4,2):]",
+        "[end-ulist:::True]",
+        "[end-block-quote:::True]",
+        "[fcode-block(5,3):`:3:block:::::]",
+        "[text(6,3):A code block:]",
+        "[end-fcode-block:::3:False]",
+        "[BLANK(8,2):]",
+        "[tbreak(9,3):-::--------]",
+        "[end-block-quote:::True]",
+        "[BLANK(10,1):]",
+    ]
+    expected_gfm = """<blockquote>
+<blockquote>
+<ul>
+<li>block 3
+block 3</li>
+<li>block 3</li>
+</ul>
+</blockquote>
+<pre><code class="language-block">A code block
+</code></pre>
+<hr />
+</blockquote>"""
+
+    # Act & Assert
+    act_and_assert(source_markdown, expected_gfm, expected_tokens, show_debug=True)
+
+
 @pytest.mark.gfm
 def test_extra_999():
     """
@@ -9545,9 +10147,12 @@ def test_extra_999():
 # Fix
 # bad_fenced_block_in_list_in_list_in_list_with_previous_block_triple_drop_with_thematics
 
+# bad_fenced_block_in_list_in_list_in_block_quote_with_previous_block_double_drop
+# test_extra_050d0 test_extra_050d1
+# - need to work with split lines
+
 # Bad fix
 # bad_fenced_block_in_list_in_list_in_block_quote_with_previous_list_double_drop_and_thematics
-# bad_fenced_block_in_list_in_list_in_block_quote_with_previous_block_double_drop
 # bad_fenced_block_in_list_in_list_in_block_quote_with_previous_block_double_drop_with_thematics
 # bad_fenced_block_in_list_in_list_in_block_quote_with_previous_block_triple_drop
 # bad_fenced_block_in_list_in_list_in_block_quote_with_previous_block_triple_drop_with_thematics