Repository: markdownlint/markdownlint (Ruby)

Issue:
MD046 vs indented code blocks
How can I put a code block in a list and also comply with MD046?

For example

````
You can break the following command into two steps

```
foo && bar
```

as follows:

1. Do foo

    ```
    foo
    ```

1. Do bar

    ```
    bar
    ```
````

violates MD046


Discussion:
OK the attached PR should fix this and has tests around it.
Fixed in 0.9.0.
I am still able to reproduce this problem on 0.11.0 using [admonitions](https://python-markdown.github.io/extensions/admonition/), for example:

```
!!! note
    This is an admonition with some code:

    ```bash
    echo "code here violates MD046"
    ```

    More text inside the admonition
```



Patch:
diff --git a/lib/mdl/doc.rb b/lib/mdl/doc.rb
index c1043c3b..9c352eb2 100644
--- a/lib/mdl/doc.rb
+++ b/lib/mdl/doc.rb
@@ -44,7 +44,7 @@ def initialize(text, ignore_front_matter = false)
       @lines = text.split(/\R/)
       @parsed = Kramdown::Document.new(text, :input => 'MarkdownLint')
       @elements = @parsed.root.children
-      add_levels(@elements)
+      add_annotations(@elements)
     end
 
     ##
@@ -277,12 +277,14 @@ def extract_text(element, prefix="", restore_whitespace = true)
     private
 
     ##
-    # Adds a 'level' option to all elements to show how nested they are
+    # Adds a 'level' and 'parent' option to all elements to show how nested they
+    # are
 
-    def add_levels(elements, level=1)
+    def add_annotations(elements, level=1, parent=nil)
       elements.each do |e|
         e.options[:element_level] = level
-        add_levels(e.children, level+1)
+        e.options[:parent] = parent
+        add_annotations(e.children, level+1, e)
       end
     end
 
diff --git a/lib/mdl/rules.rb b/lib/mdl/rules.rb
index 02498c44..3126ff97 100644
--- a/lib/mdl/rules.rb
+++ b/lib/mdl/rules.rb
@@ -669,8 +669,22 @@ def check_blockquote(errors, elements)
             style = :fenced
           end
         end
-        if @params[:style] == :fenced
-          doc.element_line(i).start_with?("    ")
+        if style == :fenced
+          # if our parent is a list or a codeblock, we need to ignore
+          # its spaces, plus 4 more
+          parent = i.options[:parent]
+          ignored_spaces = 0
+          if parent
+            parent.options.delete(:children)
+            parent.options.delete(:parent)
+            if [:li, :codeblock].include?(parent.type)
+              linenum = doc.element_linenumbers([parent]).first
+              indent = doc.indent_for(doc.lines[linenum - 1])
+              ignored_spaces = indent + 4
+            end
+          end
+          start = ' ' * ignored_spaces
+          doc.element_line(i).start_with?("#{start}    ")
         else
           !doc.element_line(i).start_with?("    ")
         end


Test patch:
diff --git a/test/rule_tests/code_block_dollar.md b/test/rule_tests/code_block_dollar.md
index 4c53cb27..3a743614 100644
--- a/test/rule_tests/code_block_dollar.md
+++ b/test/rule_tests/code_block_dollar.md
@@ -1,33 +1,43 @@
 The following code block shouldn't have $ before the commands:
 
-    $ ls {MD014}
-    $ less foo
+```bash
+$ ls
+$ less foo
 
-    $ cat bar
+$ cat bar
+```
 
 However the following code block shows output, and $ can be used to
 distinguish between command and output:
 
-    $ ls
-    foo bar
-    $ less foo
-    Hello world
+```bash
+$ ls
+foo bar
+$ less foo
+Hello world
 
-    $ cat bar
-    baz
+$ cat bar
+baz
+```
 
 The following code block uses variable names, and likewise shouldn't fire:
 
-    $foo = 'bar';
-    $baz = 'qux';
+```bash
+$foo = 'bar';
+$baz = 'qux';
+```
 
 The following code block doesn't have any dollar signs, and shouldn't fire:
 
-    ls foo
-    cat bar
+```bash
+ls foo
+cat bar
+```
 
 The following (fenced) code block doesn't have any content at all, and
 shouldn't fire:
 
 ```bash
 ```
+
+{MD014:3}
diff --git a/test/rule_tests/consecutive_blank_lines.md b/test/rule_tests/consecutive_blank_lines.md
index 9d403503..77ffb19a 100644
--- a/test/rule_tests/consecutive_blank_lines.md
+++ b/test/rule_tests/consecutive_blank_lines.md
@@ -3,9 +3,11 @@ Some text
 
 Some text {MD012:3}
 
-    This is a code block
+```fenced
+This is a code block
 
 
-    with two blank lines in it
+with two blank lines in it
+```
 
-Some more text
\ No newline at end of file
+Some more text
diff --git a/test/rule_tests/default_test_style.rb b/test/rule_tests/default_test_style.rb
index 4ad18844..5999d4b3 100644
--- a/test/rule_tests/default_test_style.rb
+++ b/test/rule_tests/default_test_style.rb
@@ -2,4 +2,4 @@
 all
 
 exclude_rule "MD041"
-exclude_rule "MD046"
+#exclude_rule "MD046"
diff --git a/test/rule_tests/fenced_code_blocks_in_lists.md b/test/rule_tests/fenced_code_blocks_in_lists.md
new file mode 100644
index 00000000..ed5302a7
--- /dev/null
+++ b/test/rule_tests/fenced_code_blocks_in_lists.md
@@ -0,0 +1,40 @@
+# test doc
+
+this is some text
+
+* This is a list item
+
+    ```fenced
+    this is a code block within the list item.
+    ```
+
+    with more text here
+
+* and another list item here
+
+And another paragraph.
+
+    But this code block {MD046}
+
+    is *NOT* in a list and should error.
+
+And in addition to that...
+
+    ```text
+    This code block is both indented
+    and fenced and should *also* error.
+    ```
+
+And finally:
+
+```text
+This is a code block
+
+    And this is a code block in a code block and should *not* error
+
+More stuff here
+```
+
+all
+
+{MD046:23}
diff --git a/test/rule_tests/inline_html_style.rb b/test/rule_tests/inline_html_style.rb
new file mode 100644
index 00000000..96d3a2da
--- /dev/null
+++ b/test/rule_tests/inline_html_style.rb
@@ -0,0 +1,2 @@
+all
+exclude_rule 'MD046'
diff --git a/test/rule_tests/reversed_link.md b/test/rule_tests/reversed_link.md
index 7184ecbe..8fa769c1 100644
--- a/test/rule_tests/reversed_link.md
+++ b/test/rule_tests/reversed_link.md
@@ -2,6 +2,8 @@ Go to (this website)[http://www.example.com] {MD011} {MD034}
 
 However, this shouldn't trigger inside code blocks:
 
-    myObj.getFiles("test")[0]
+```fenced
+myObj.getFiles("test")[0]
+```
 
 Nor inline code: `myobj.getFiles("test")[0]`